mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-04 12:01:12 +02:00
feat: move host DNS config into ResolverConfig
This deprecates more `.machine.features`, allows host DNS to be enabled in maintenance mode. Fixes #12438 Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
parent
96a8ecd1ee
commit
837a9ed077
@ -19,6 +19,7 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/generate"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/generate/secrets"
|
||||
"github.com/siderolabs/talos/pkg/machinery/version"
|
||||
"github.com/siderolabs/talos/pkg/provision"
|
||||
)
|
||||
|
||||
@ -150,7 +151,8 @@ func TestCommonMaker_MachineConfig(t *testing.T) {
|
||||
|
||||
// assertConfigDefaultness makes sure the maker-generated machine configs are not different from default talos machine configs.
|
||||
func assertConfigDefaultness[ExtraOps any](t *testing.T, cOps clusterops.Common, m makers.Maker[ExtraOps], desiredExtraGenOps []generate.Option, extraPatches ...configpatcher.Patch) {
|
||||
var versionContract *config.VersionContract
|
||||
versionContract, err := config.ParseContractFromVersion(version.Tag)
|
||||
require.NoError(t, err)
|
||||
|
||||
secretsBundle, err := secrets.NewBundle(secrets.NewClock(), versionContract)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -45,6 +45,12 @@ Custom settings for cipher suites have been removed, as they are ignored when TL
|
||||
title = "Default Installer Image"
|
||||
description = """\
|
||||
The default installer image has been updated to use the Image Factory.
|
||||
"""
|
||||
|
||||
[notes.hostdnsconfig]
|
||||
title = "Host DNS Configuration"
|
||||
description = """\
|
||||
HostDNS configuration was moved from the v1alpha1 config `.machine.features.hostDNS` field to the new `hostDNS` in the `ResolverConfig` document.
|
||||
"""
|
||||
|
||||
[make_deps]
|
||||
|
||||
@ -17,7 +17,7 @@ import (
|
||||
"github.com/siderolabs/go-procfs/procfs"
|
||||
"go.uber.org/zap"
|
||||
|
||||
talosconfig "github.com/siderolabs/talos/pkg/machinery/config"
|
||||
cfgcfg "github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
@ -71,8 +71,6 @@ func (ctrl *HostDNSConfigController) Run(ctx context.Context, r controller.Runti
|
||||
case <-r.EventCh():
|
||||
}
|
||||
|
||||
var cfgProvider talosconfig.Config
|
||||
|
||||
r.StartTrackingOutputs()
|
||||
|
||||
cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID)
|
||||
@ -80,8 +78,12 @@ func (ctrl *HostDNSConfigController) Run(ctx context.Context, r controller.Runti
|
||||
if !state.IsNotFoundError(err) {
|
||||
return fmt.Errorf("error getting config: %w", err)
|
||||
}
|
||||
} else if cfg.Config().Machine() != nil {
|
||||
cfgProvider = cfg.Config()
|
||||
}
|
||||
|
||||
var hostDNSConfig cfgcfg.NetworkHostDNSConfig
|
||||
|
||||
if cfg != nil {
|
||||
hostDNSConfig = cfg.Config().NetworkHostDNSConfig()
|
||||
}
|
||||
|
||||
newServiceAddrs := make([]netip.Addr, 0, 2)
|
||||
@ -93,21 +95,27 @@ func (ctrl *HostDNSConfigController) Run(ctx context.Context, r controller.Runti
|
||||
|
||||
res.TypedSpec().ServiceHostDNSAddress = netip.Addr{}
|
||||
|
||||
if cfgProvider == nil {
|
||||
if hostDNSConfig == nil {
|
||||
res.TypedSpec().Enabled = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
res.TypedSpec().Enabled = cfgProvider.Machine().Features().HostDNS().Enabled()
|
||||
res.TypedSpec().ResolveMemberNames = cfgProvider.Machine().Features().HostDNS().ResolveMemberNames()
|
||||
res.TypedSpec().Enabled = hostDNSConfig.HostDNSEnabled()
|
||||
res.TypedSpec().ResolveMemberNames = hostDNSConfig.ResolveMemberNames()
|
||||
|
||||
if !cfgProvider.Machine().Features().HostDNS().ForwardKubeDNSToHost() {
|
||||
if !hostDNSConfig.ForwardKubeDNSToHost() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var podCIDRs []string
|
||||
|
||||
if cfg.Config().Cluster() != nil {
|
||||
podCIDRs = cfg.Config().Cluster().Network().PodCIDRs()
|
||||
}
|
||||
|
||||
if slices.ContainsFunc(
|
||||
cfgProvider.Cluster().Network().PodCIDRs(),
|
||||
podCIDRs,
|
||||
func(cidr string) bool { return netip.MustParsePrefix(cidr).Addr().Is4() },
|
||||
) {
|
||||
parsed := netip.MustParseAddr(constants.HostDNSAddress)
|
||||
|
||||
@ -0,0 +1,263 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package network_test
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/resource/rtestutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
|
||||
netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/container"
|
||||
networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
)
|
||||
|
||||
type HostDNSConfigSuite struct {
|
||||
ctest.DefaultSuite
|
||||
}
|
||||
|
||||
func (suite *HostDNSConfigSuite) TestNoConfig() {
|
||||
ctest.AssertResource(suite, network.HostDNSConfigID, func(r *network.HostDNSConfig, asrt *assert.Assertions) {
|
||||
asrt.False(r.TypedSpec().Enabled)
|
||||
asrt.Equal(
|
||||
[]netip.AddrPort{netip.MustParseAddrPort("127.0.0.53:53")},
|
||||
r.TypedSpec().ListenAddresses,
|
||||
)
|
||||
asrt.Equal(netip.Addr{}, r.TypedSpec().ServiceHostDNSAddress)
|
||||
asrt.False(r.TypedSpec().ResolveMemberNames)
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *HostDNSConfigSuite) TestLegacyConfigEnabled() {
|
||||
u, err := url.Parse("https://foo:6443")
|
||||
suite.Require().NoError(err)
|
||||
|
||||
cfg := config.NewMachineConfig(
|
||||
container.NewV1Alpha1(
|
||||
&v1alpha1.Config{
|
||||
ConfigVersion: "v1alpha1",
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineFeatures: &v1alpha1.FeaturesConfig{
|
||||
HostDNSSupport: &v1alpha1.HostDNSConfig{ //nolint:staticcheck // testing legacy config
|
||||
HostDNSConfigEnabled: new(true),
|
||||
HostDNSResolveMemberNames: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
ClusterConfig: &v1alpha1.ClusterConfig{
|
||||
ControlPlane: &v1alpha1.ControlPlaneConfig{
|
||||
Endpoint: &v1alpha1.Endpoint{URL: u},
|
||||
},
|
||||
ClusterNetwork: &v1alpha1.ClusterNetworkConfig{
|
||||
PodSubnet: []string{constants.DefaultIPv4PodNet},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
suite.Create(cfg)
|
||||
|
||||
ctest.AssertResource(suite, network.HostDNSConfigID, func(r *network.HostDNSConfig, asrt *assert.Assertions) {
|
||||
asrt.True(r.TypedSpec().Enabled)
|
||||
asrt.Equal(
|
||||
[]netip.AddrPort{netip.MustParseAddrPort("127.0.0.53:53")},
|
||||
r.TypedSpec().ListenAddresses,
|
||||
)
|
||||
asrt.Equal(netip.Addr{}, r.TypedSpec().ServiceHostDNSAddress)
|
||||
asrt.True(r.TypedSpec().ResolveMemberNames)
|
||||
})
|
||||
|
||||
ctest.AssertNoResource[*network.AddressSpec](
|
||||
suite,
|
||||
network.LayeredID(network.ConfigOperator, network.AddressID("lo", netip.MustParsePrefix(constants.HostDNSAddress+"/32"))),
|
||||
rtestutils.WithNamespace(network.ConfigNamespaceName),
|
||||
)
|
||||
}
|
||||
|
||||
func (suite *HostDNSConfigSuite) TestLegacyConfigForwardKubeDNSIPv4() {
|
||||
u, err := url.Parse("https://foo:6443")
|
||||
suite.Require().NoError(err)
|
||||
|
||||
cfg := config.NewMachineConfig(
|
||||
container.NewV1Alpha1(
|
||||
&v1alpha1.Config{
|
||||
ConfigVersion: "v1alpha1",
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineFeatures: &v1alpha1.FeaturesConfig{
|
||||
HostDNSSupport: &v1alpha1.HostDNSConfig{ //nolint:staticcheck // testing legacy config
|
||||
HostDNSConfigEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
ClusterConfig: &v1alpha1.ClusterConfig{
|
||||
ControlPlane: &v1alpha1.ControlPlaneConfig{
|
||||
Endpoint: &v1alpha1.Endpoint{URL: u},
|
||||
},
|
||||
ClusterNetwork: &v1alpha1.ClusterNetworkConfig{
|
||||
PodSubnet: []string{constants.DefaultIPv4PodNet, constants.DefaultIPv6PodNet},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
suite.Create(cfg)
|
||||
|
||||
hostDNSAddr := netip.MustParseAddr(constants.HostDNSAddress)
|
||||
|
||||
ctest.AssertResource(suite, network.HostDNSConfigID, func(r *network.HostDNSConfig, asrt *assert.Assertions) {
|
||||
asrt.True(r.TypedSpec().Enabled)
|
||||
asrt.Equal(
|
||||
[]netip.AddrPort{
|
||||
netip.MustParseAddrPort("127.0.0.53:53"),
|
||||
netip.AddrPortFrom(hostDNSAddr, 53),
|
||||
},
|
||||
r.TypedSpec().ListenAddresses,
|
||||
)
|
||||
asrt.Equal(hostDNSAddr, r.TypedSpec().ServiceHostDNSAddress)
|
||||
})
|
||||
|
||||
addrPrefix := netip.PrefixFrom(hostDNSAddr, hostDNSAddr.BitLen())
|
||||
|
||||
ctest.AssertResource(
|
||||
suite,
|
||||
network.LayeredID(network.ConfigOperator, network.AddressID("lo", addrPrefix)),
|
||||
func(r *network.AddressSpec, asrt *assert.Assertions) {
|
||||
spec := r.TypedSpec()
|
||||
|
||||
asrt.Equal(addrPrefix, spec.Address)
|
||||
asrt.Equal("lo", spec.LinkName)
|
||||
asrt.Equal(nethelpers.FamilyInet4, spec.Family)
|
||||
asrt.Equal(nethelpers.ScopeHost, spec.Scope)
|
||||
asrt.Equal(nethelpers.AddressFlags(nethelpers.AddressPermanent), spec.Flags)
|
||||
asrt.Equal(network.ConfigOperator, spec.ConfigLayer)
|
||||
},
|
||||
rtestutils.WithNamespace(network.ConfigNamespaceName),
|
||||
)
|
||||
}
|
||||
|
||||
func (suite *HostDNSConfigSuite) TestLegacyConfigForwardKubeDNSIPv6Only() {
|
||||
u, err := url.Parse("https://foo:6443")
|
||||
suite.Require().NoError(err)
|
||||
|
||||
cfg := config.NewMachineConfig(
|
||||
container.NewV1Alpha1(
|
||||
&v1alpha1.Config{
|
||||
ConfigVersion: "v1alpha1",
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineFeatures: &v1alpha1.FeaturesConfig{
|
||||
HostDNSSupport: &v1alpha1.HostDNSConfig{ //nolint:staticcheck // testing legacy config
|
||||
HostDNSConfigEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
ClusterConfig: &v1alpha1.ClusterConfig{
|
||||
ControlPlane: &v1alpha1.ControlPlaneConfig{
|
||||
Endpoint: &v1alpha1.Endpoint{URL: u},
|
||||
},
|
||||
ClusterNetwork: &v1alpha1.ClusterNetworkConfig{
|
||||
PodSubnet: []string{constants.DefaultIPv6PodNet},
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
suite.Create(cfg)
|
||||
|
||||
ctest.AssertResource(suite, network.HostDNSConfigID, func(r *network.HostDNSConfig, asrt *assert.Assertions) {
|
||||
asrt.True(r.TypedSpec().Enabled)
|
||||
asrt.Equal(
|
||||
[]netip.AddrPort{netip.MustParseAddrPort("127.0.0.53:53")},
|
||||
r.TypedSpec().ListenAddresses,
|
||||
)
|
||||
asrt.Equal(netip.Addr{}, r.TypedSpec().ServiceHostDNSAddress)
|
||||
})
|
||||
|
||||
ctest.AssertNoResource[*network.AddressSpec](
|
||||
suite,
|
||||
network.LayeredID(network.ConfigOperator, network.AddressID("lo", netip.MustParsePrefix(constants.HostDNSAddress+"/32"))),
|
||||
rtestutils.WithNamespace(network.ConfigNamespaceName),
|
||||
)
|
||||
}
|
||||
|
||||
func (suite *HostDNSConfigSuite) TestResolverConfigDocument() {
|
||||
rc := networkcfg.NewResolverConfigV1Alpha1()
|
||||
rc.ResolverHostDNS = networkcfg.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
HostDNSResolveMemberNames: new(true),
|
||||
}
|
||||
|
||||
v1 := &v1alpha1.Config{
|
||||
ConfigVersion: "v1alpha1",
|
||||
MachineConfig: &v1alpha1.MachineConfig{},
|
||||
ClusterConfig: &v1alpha1.ClusterConfig{
|
||||
ClusterNetwork: &v1alpha1.ClusterNetworkConfig{
|
||||
PodSubnet: []string{constants.DefaultIPv4PodNet},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctr, err := container.New(v1, rc)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.Create(config.NewMachineConfig(ctr))
|
||||
|
||||
hostDNSAddr := netip.MustParseAddr(constants.HostDNSAddress)
|
||||
|
||||
ctest.AssertResource(suite, network.HostDNSConfigID, func(r *network.HostDNSConfig, asrt *assert.Assertions) {
|
||||
asrt.True(r.TypedSpec().Enabled)
|
||||
asrt.True(r.TypedSpec().ResolveMemberNames)
|
||||
asrt.Equal(
|
||||
[]netip.AddrPort{
|
||||
netip.MustParseAddrPort("127.0.0.53:53"),
|
||||
netip.AddrPortFrom(hostDNSAddr, 53),
|
||||
},
|
||||
r.TypedSpec().ListenAddresses,
|
||||
)
|
||||
asrt.Equal(hostDNSAddr, r.TypedSpec().ServiceHostDNSAddress)
|
||||
})
|
||||
|
||||
addrPrefix := netip.PrefixFrom(hostDNSAddr, hostDNSAddr.BitLen())
|
||||
|
||||
ctest.AssertResource(
|
||||
suite,
|
||||
network.LayeredID(network.ConfigOperator, network.AddressID("lo", addrPrefix)),
|
||||
func(r *network.AddressSpec, asrt *assert.Assertions) {
|
||||
asrt.Equal(addrPrefix, r.TypedSpec().Address)
|
||||
asrt.Equal(nethelpers.FamilyInet4, r.TypedSpec().Family)
|
||||
asrt.Equal("lo", r.TypedSpec().LinkName)
|
||||
},
|
||||
rtestutils.WithNamespace(network.ConfigNamespaceName),
|
||||
)
|
||||
}
|
||||
|
||||
func TestHostDNSConfigSuite(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
suite.Run(t, &HostDNSConfigSuite{
|
||||
DefaultSuite: ctest.DefaultSuite{
|
||||
Timeout: 5 * time.Second,
|
||||
AfterSetup: func(s *ctest.DefaultSuite) {
|
||||
s.Require().NoError(s.Runtime().RegisterController(&netctrl.HostDNSConfigController{}))
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -207,8 +207,8 @@ func (ctrl *NfTablesChainConfigController) buildIngressChain(cfg *config.Machine
|
||||
},
|
||||
)
|
||||
|
||||
if cfg.Config().Machine() != nil && cfg.Config().Cluster() != nil {
|
||||
if cfg.Config().Machine().Features().HostDNS().ForwardKubeDNSToHost() {
|
||||
if hostDNSConfig := cfg.Config().NetworkHostDNSConfig(); hostDNSConfig != nil {
|
||||
if hostDNSConfig.ForwardKubeDNSToHost() {
|
||||
hostDNSIP := netip.MustParseAddr(constants.HostDNSAddress)
|
||||
|
||||
// allow traffic to host DNS
|
||||
|
||||
@ -198,7 +198,7 @@ func (r *Runtime) CanApplyImmediate(cfg config.Provider) error {
|
||||
if newConfig.MachineConfig.MachineFeatures != nil && currentConfig.MachineConfig.MachineFeatures != nil {
|
||||
newConfig.MachineConfig.MachineFeatures.KubernetesTalosAPIAccessConfig = currentConfig.MachineConfig.MachineFeatures.KubernetesTalosAPIAccessConfig
|
||||
newConfig.MachineConfig.MachineFeatures.KubePrismSupport = currentConfig.MachineConfig.MachineFeatures.KubePrismSupport
|
||||
newConfig.MachineConfig.MachineFeatures.HostDNSSupport = currentConfig.MachineConfig.MachineFeatures.HostDNSSupport
|
||||
newConfig.MachineConfig.MachineFeatures.HostDNSSupport = currentConfig.MachineConfig.MachineFeatures.HostDNSSupport //nolint:staticcheck // backwards compatibility
|
||||
newConfig.MachineConfig.MachineFeatures.ImageCacheSupport = currentConfig.MachineConfig.MachineFeatures.ImageCacheSupport
|
||||
newConfig.MachineConfig.MachineFeatures.FeatureNodeAddressSortAlgorithm = currentConfig.MachineConfig.MachineFeatures.FeatureNodeAddressSortAlgorithm
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ type Config interface { //nolint:interfacebloat
|
||||
NetworkStaticHostConfig() []NetworkStaticHostConfig
|
||||
NetworkHostnameConfig() NetworkHostnameConfig
|
||||
NetworkResolverConfig() NetworkResolverConfig
|
||||
NetworkHostDNSConfig() NetworkHostDNSConfig
|
||||
NetworkTimeSyncConfig() NetworkTimeSyncConfig
|
||||
NetworkKubeSpanConfig() NetworkKubeSpanConfig
|
||||
NetworkCommonLinkConfigs() []NetworkCommonLinkConfig
|
||||
|
||||
@ -378,7 +378,6 @@ type SystemDiskEncryption interface {
|
||||
type Features interface {
|
||||
KubernetesTalosAPIAccess() KubernetesTalosAPIAccess
|
||||
DiskQuotaSupportEnabled() bool
|
||||
HostDNS() HostDNS
|
||||
KubePrism() KubePrism
|
||||
ImageCache() ImageCache
|
||||
NodeAddressSortAlgorithm() nethelpers.AddressSortAlgorithm
|
||||
@ -397,13 +396,6 @@ type KubePrism interface {
|
||||
Port() int
|
||||
}
|
||||
|
||||
// HostDNS describes the host DNS configuration.
|
||||
type HostDNS interface {
|
||||
Enabled() bool
|
||||
ForwardKubeDNSToHost() bool
|
||||
ResolveMemberNames() bool
|
||||
}
|
||||
|
||||
// ImageCache describes the image cache configuration.
|
||||
type ImageCache interface {
|
||||
LocalEnabled() bool
|
||||
|
||||
@ -381,3 +381,10 @@ type NetworkRoutingRuleConfig interface {
|
||||
FwMark() uint32
|
||||
FwMask() uint32
|
||||
}
|
||||
|
||||
// NetworkHostDNSConfig defines a host DNS configuration.
|
||||
type NetworkHostDNSConfig interface {
|
||||
HostDNSEnabled() bool
|
||||
ForwardKubeDNSToHost() bool
|
||||
ResolveMemberNames() bool
|
||||
}
|
||||
|
||||
@ -7,21 +7,17 @@ package container
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/siderolabs/gen/xslices"
|
||||
|
||||
coreconfig "github.com/siderolabs/talos/pkg/machinery/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/validation"
|
||||
)
|
||||
|
||||
// V1Alpha1ConflictValidator is the interface implemented by config documents which conflict with legacy v1alpha1 config.
|
||||
@ -328,6 +324,23 @@ func (container *Container) NetworkResolverConfig() config.NetworkResolverConfig
|
||||
return nil
|
||||
}
|
||||
|
||||
// NetworkHostDNSConfig implements config.Config interface.
|
||||
func (container *Container) NetworkHostDNSConfig() config.NetworkHostDNSConfig {
|
||||
// first check if we have a dedicated document, and it is not empty
|
||||
// for backwards compatibility, we will fall back to v1alpha1 if the ResolverConfig document does not have hostDNS enabled
|
||||
matching := findMatchingDocs[config.NetworkHostDNSConfig](container.documents)
|
||||
if len(matching) > 0 && matching[0].HostDNSEnabled() {
|
||||
return matching[0]
|
||||
}
|
||||
|
||||
// fallback to v1alpha1
|
||||
if container.v1alpha1Config != nil {
|
||||
return container.v1alpha1Config.NetworkHostDNSConfig()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NetworkTimeSyncConfig implements config.Config interface.
|
||||
func (container *Container) NetworkTimeSyncConfig() config.NetworkTimeSyncConfig {
|
||||
// first check if we have a dedicated document
|
||||
@ -577,90 +590,6 @@ func docID(doc config.Document) string {
|
||||
return id
|
||||
}
|
||||
|
||||
// Validate checks configuration and returns warnings and fatal errors (as multierror).
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (container *Container) Validate(mode validation.RuntimeMode, opt ...validation.Option) ([]string, error) {
|
||||
var (
|
||||
warnings []string
|
||||
err error
|
||||
)
|
||||
|
||||
if container.v1alpha1Config != nil {
|
||||
warnings, err = container.v1alpha1Config.Validate(mode, opt...)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("v1alpha1.Config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var multiErr *multierror.Error
|
||||
|
||||
if err != nil {
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
}
|
||||
|
||||
for _, doc := range container.documents {
|
||||
if validatableDoc, ok := doc.(config.Validator); ok {
|
||||
docWarnings, docErr := validatableDoc.Validate(mode, opt...)
|
||||
if docErr != nil {
|
||||
docErr = fmt.Errorf("%s: %w", docID(doc), docErr)
|
||||
}
|
||||
|
||||
warnings = append(warnings, docWarnings...)
|
||||
multiErr = multierror.Append(multiErr, docErr)
|
||||
}
|
||||
}
|
||||
|
||||
// now cross-validate the config
|
||||
if container.v1alpha1Config != nil {
|
||||
for _, doc := range container.documents {
|
||||
if conflictValidator, ok := doc.(V1Alpha1ConflictValidator); ok {
|
||||
err := conflictValidator.V1Alpha1ConflictValidate(container.v1alpha1Config)
|
||||
if err != nil {
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return warnings, multiErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// RuntimeValidate validates the config in the runtime context.
|
||||
func (container *Container) RuntimeValidate(ctx context.Context, st state.State, mode validation.RuntimeMode, opt ...validation.Option) ([]string, error) {
|
||||
var (
|
||||
warnings []string
|
||||
err error
|
||||
)
|
||||
|
||||
if container.v1alpha1Config != nil {
|
||||
warnings, err = container.v1alpha1Config.RuntimeValidate(ctx, st, mode, opt...)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("v1alpha1.Config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var multiErr *multierror.Error
|
||||
|
||||
if err != nil {
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
}
|
||||
|
||||
for _, doc := range container.documents {
|
||||
if validatableDoc, ok := doc.(config.RuntimeValidator); ok {
|
||||
docWarnings, docErr := validatableDoc.RuntimeValidate(ctx, st, mode, opt...)
|
||||
if docErr != nil {
|
||||
docErr = fmt.Errorf("%s: %w", docID(doc), docErr)
|
||||
}
|
||||
|
||||
warnings = append(warnings, docWarnings...)
|
||||
multiErr = multierror.Append(multiErr, docErr)
|
||||
}
|
||||
}
|
||||
|
||||
return warnings, multiErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// RedactSecrets returns a copy of the Provider with all secrets replaced with the given string.
|
||||
func (container *Container) RedactSecrets(replacement string) coreconfig.Provider {
|
||||
clone := container.clone()
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/siderolabs/crypto/x509"
|
||||
"github.com/siderolabs/gen/xtesting/must"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -23,8 +22,6 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/runtime/extensions"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/siderolink"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
blockres "github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
@ -153,197 +150,6 @@ func TestPatchV1Alpha1(t *testing.T) {
|
||||
assert.Equal(t, "https://siderolink.api/?jointoken=secret&user=alice", patchedCfg.SideroLink().APIUrl().String())
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sideroLinkCfg := siderolink.NewConfigV1Alpha1()
|
||||
sideroLinkCfg.APIUrlConfig.URL = must.Value(url.Parse("https://siderolink.api/?jointoken=secret&user=alice"))(t)
|
||||
|
||||
invalidSideroLinkCfg := siderolink.NewConfigV1Alpha1()
|
||||
|
||||
v1alpha1Cfg := &v1alpha1.Config{
|
||||
ClusterConfig: &v1alpha1.ClusterConfig{
|
||||
ControlPlane: &v1alpha1.ControlPlaneConfig{
|
||||
Endpoint: &v1alpha1.Endpoint{
|
||||
URL: must.Value(url.Parse("https://localhost:6443"))(t),
|
||||
},
|
||||
},
|
||||
},
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineType: "worker",
|
||||
MachineCA: &x509.PEMEncodedCertificateAndKey{
|
||||
Crt: []byte("cert"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
invalidV1alpha1Config := &v1alpha1.Config{}
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
documents []config.Document
|
||||
|
||||
expectedError string
|
||||
expecetedWarnings []string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
},
|
||||
{
|
||||
name: "multi-doc",
|
||||
documents: []config.Document{sideroLinkCfg, v1alpha1Cfg},
|
||||
},
|
||||
{
|
||||
name: "only siderolink",
|
||||
documents: []config.Document{sideroLinkCfg},
|
||||
},
|
||||
{
|
||||
name: "only v1alpha1",
|
||||
documents: []config.Document{v1alpha1Cfg},
|
||||
},
|
||||
{
|
||||
name: "invalid siderolink",
|
||||
documents: []config.Document{invalidSideroLinkCfg},
|
||||
expectedError: "1 error occurred:\n\t* SideroLinkConfig: apiUrl is required\n\n",
|
||||
},
|
||||
{
|
||||
name: "invalid v1alpha1",
|
||||
documents: []config.Document{invalidV1alpha1Config},
|
||||
expectedError: "1 error occurred:\n\t* v1alpha1.Config: 1 error occurred:\n\t* machine instructions are required\n\n\n\n",
|
||||
},
|
||||
{
|
||||
name: "invalid multi-doc",
|
||||
documents: []config.Document{invalidSideroLinkCfg, invalidV1alpha1Config},
|
||||
expectedError: "2 errors occurred:\n\t* v1alpha1.Config: 1 error occurred:\n\t* machine instructions are required\n\n\n\t* SideroLinkConfig: apiUrl is required\n\n",
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctr, err := container.New(tt.documents...)
|
||||
require.NoError(t, err)
|
||||
|
||||
warnings, err := ctr.Validate(validationMode{})
|
||||
|
||||
if tt.expectedError == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.EqualError(t, err, tt.expectedError)
|
||||
}
|
||||
|
||||
require.Equal(t, tt.expecetedWarnings, warnings)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossValidateEncryption(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
v1alpha1Cfg := &v1alpha1.Config{
|
||||
ClusterConfig: &v1alpha1.ClusterConfig{
|
||||
ControlPlane: &v1alpha1.ControlPlaneConfig{
|
||||
Endpoint: &v1alpha1.Endpoint{
|
||||
URL: must.Value(url.Parse("https://localhost:6443"))(t),
|
||||
},
|
||||
},
|
||||
},
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineType: "worker",
|
||||
MachineCA: &x509.PEMEncodedCertificateAndKey{
|
||||
Crt: []byte("cert"),
|
||||
},
|
||||
MachineSystemDiskEncryption: &v1alpha1.SystemDiskEncryptionConfig{
|
||||
EphemeralPartition: &v1alpha1.EncryptionConfig{
|
||||
EncryptionKeys: []*v1alpha1.EncryptionKey{
|
||||
{
|
||||
KeySlot: 1,
|
||||
KeyStatic: &v1alpha1.EncryptionKeyStatic{
|
||||
KeyData: "static-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
defaultEphemeral := block.NewVolumeConfigV1Alpha1()
|
||||
defaultEphemeral.MetaName = constants.EphemeralPartitionLabel
|
||||
|
||||
encryptedEphemeral := block.NewVolumeConfigV1Alpha1()
|
||||
encryptedEphemeral.MetaName = constants.EphemeralPartitionLabel
|
||||
encryptedEphemeral.EncryptionSpec = block.EncryptionSpec{
|
||||
EncryptionProvider: blockres.EncryptionProviderLUKS2,
|
||||
EncryptionKeys: []block.EncryptionKey{
|
||||
{
|
||||
KeySlot: 2,
|
||||
KeyStatic: &block.EncryptionKeyStatic{
|
||||
KeyData: "encrypted-static-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
encryptedState := block.NewVolumeConfigV1Alpha1()
|
||||
encryptedState.MetaName = constants.StatePartitionLabel
|
||||
encryptedState.EncryptionSpec = block.EncryptionSpec{
|
||||
EncryptionProvider: blockres.EncryptionProviderLUKS2,
|
||||
EncryptionKeys: []block.EncryptionKey{
|
||||
{
|
||||
KeySlot: 3,
|
||||
KeyTPM: &block.EncryptionKeyTPM{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
documents []config.Document
|
||||
|
||||
expectedError string
|
||||
expecetedWarnings []string
|
||||
}{
|
||||
{
|
||||
name: "only v1alpha1",
|
||||
documents: []config.Document{v1alpha1Cfg},
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 with no-conflict volumes",
|
||||
documents: []config.Document{v1alpha1Cfg, defaultEphemeral, encryptedState},
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 with no-conflict volumes",
|
||||
documents: []config.Document{v1alpha1Cfg, encryptedState},
|
||||
},
|
||||
{
|
||||
name: "no v1alpha1",
|
||||
documents: []config.Document{encryptedEphemeral, encryptedState},
|
||||
},
|
||||
{
|
||||
name: "conflict on ephemeral encryption",
|
||||
documents: []config.Document{v1alpha1Cfg, encryptedEphemeral},
|
||||
expectedError: "1 error occurred:\n\t* system disk encryption for \"EPHEMERAL\" is configured in both v1alpha1.Config and VolumeConfig\n\n",
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctr, err := container.New(tt.documents...)
|
||||
require.NoError(t, err)
|
||||
|
||||
warnings, err := ctr.Validate(validationMode{})
|
||||
|
||||
if tt.expectedError == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.EqualError(t, err, tt.expectedError)
|
||||
}
|
||||
|
||||
require.Equal(t, tt.expecetedWarnings, warnings)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunDefaultDHCPOperators(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@ -397,17 +203,3 @@ func TestRunDefaultDHCPOperators(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type validationMode struct{}
|
||||
|
||||
func (validationMode) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (validationMode) RequiresInstall() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (validationMode) InContainer() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
134
pkg/machinery/config/container/validate.go
Normal file
134
pkg/machinery/config/container/validate.go
Normal file
@ -0,0 +1,134 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/validation"
|
||||
)
|
||||
|
||||
// Validate checks configuration and returns warnings and fatal errors (as multierror).
|
||||
//
|
||||
// The validation first validates each individual document, then it does conflict validation of new
|
||||
// documents with v1alpha1.Config (if it exists).
|
||||
// Finally, whole container is validated according to the mode.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (container *Container) Validate(mode validation.RuntimeMode, opt ...validation.Option) ([]string, error) {
|
||||
var (
|
||||
warnings []string
|
||||
err error
|
||||
)
|
||||
|
||||
if container.v1alpha1Config != nil {
|
||||
warnings, err = container.v1alpha1Config.Validate(mode, opt...)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("v1alpha1.Config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var multiErr *multierror.Error
|
||||
|
||||
if err != nil {
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
}
|
||||
|
||||
for _, doc := range container.documents {
|
||||
if validatableDoc, ok := doc.(config.Validator); ok {
|
||||
docWarnings, docErr := validatableDoc.Validate(mode, opt...)
|
||||
if docErr != nil {
|
||||
docErr = fmt.Errorf("%s: %w", docID(doc), docErr)
|
||||
}
|
||||
|
||||
warnings = append(warnings, docWarnings...)
|
||||
multiErr = multierror.Append(multiErr, docErr)
|
||||
}
|
||||
}
|
||||
|
||||
// now cross-validate the config
|
||||
if container.v1alpha1Config != nil {
|
||||
for _, doc := range container.documents {
|
||||
if conflictValidator, ok := doc.(V1Alpha1ConflictValidator); ok {
|
||||
err := conflictValidator.V1Alpha1ConflictValidate(container.v1alpha1Config)
|
||||
if err != nil {
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := container.validateContainer(mode); err != nil {
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
}
|
||||
|
||||
return warnings, multiErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// RuntimeValidate validates the config in the runtime context.
|
||||
func (container *Container) RuntimeValidate(ctx context.Context, st state.State, mode validation.RuntimeMode, opt ...validation.Option) ([]string, error) {
|
||||
var (
|
||||
warnings []string
|
||||
err error
|
||||
)
|
||||
|
||||
if container.v1alpha1Config != nil {
|
||||
warnings, err = container.v1alpha1Config.RuntimeValidate(ctx, st, mode, opt...)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("v1alpha1.Config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var multiErr *multierror.Error
|
||||
|
||||
if err != nil {
|
||||
multiErr = multierror.Append(multiErr, err)
|
||||
}
|
||||
|
||||
for _, doc := range container.documents {
|
||||
if validatableDoc, ok := doc.(config.RuntimeValidator); ok {
|
||||
docWarnings, docErr := validatableDoc.RuntimeValidate(ctx, st, mode, opt...)
|
||||
if docErr != nil {
|
||||
docErr = fmt.Errorf("%s: %w", docID(doc), docErr)
|
||||
}
|
||||
|
||||
warnings = append(warnings, docWarnings...)
|
||||
multiErr = multierror.Append(multiErr, docErr)
|
||||
}
|
||||
}
|
||||
|
||||
return warnings, multiErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// validateContainer validates the full configuration container.
|
||||
//
|
||||
// This validation is used to do validation which only makes sense for the full configuration (vs. individual documents).
|
||||
func (container *Container) validateContainer(mode validation.RuntimeMode) error {
|
||||
var errs error
|
||||
|
||||
if mode.InContainer() {
|
||||
// in container mode, HostDNS must be enabled and forward KubeDNS to host must be enabled as well
|
||||
hostDNSConfig := container.NetworkHostDNSConfig()
|
||||
|
||||
if hostDNSConfig == nil {
|
||||
errs = multierror.Append(errs, fmt.Errorf("hostDNS config is required in container mode"))
|
||||
} else {
|
||||
if !hostDNSConfig.HostDNSEnabled() {
|
||||
errs = multierror.Append(errs, fmt.Errorf("hostDNS must be enabled in container mode"))
|
||||
}
|
||||
|
||||
if !hostDNSConfig.ForwardKubeDNSToHost() {
|
||||
errs = multierror.Append(errs, fmt.Errorf("forwardKubeDNSToHost must be enabled in container mode"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
344
pkg/machinery/config/container/validate_test.go
Normal file
344
pkg/machinery/config/container/validate_test.go
Normal file
@ -0,0 +1,344 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package container_test
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/siderolabs/crypto/x509"
|
||||
"github.com/siderolabs/gen/xtesting/must"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/container"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/network"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/siderolink"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
blockres "github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
)
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sideroLinkCfg := siderolink.NewConfigV1Alpha1()
|
||||
sideroLinkCfg.APIUrlConfig.URL = must.Value(url.Parse("https://siderolink.api/?jointoken=secret&user=alice"))(t)
|
||||
|
||||
invalidSideroLinkCfg := siderolink.NewConfigV1Alpha1()
|
||||
|
||||
v1alpha1Cfg := &v1alpha1.Config{
|
||||
ClusterConfig: &v1alpha1.ClusterConfig{
|
||||
ControlPlane: &v1alpha1.ControlPlaneConfig{
|
||||
Endpoint: &v1alpha1.Endpoint{
|
||||
URL: must.Value(url.Parse("https://localhost:6443"))(t),
|
||||
},
|
||||
},
|
||||
},
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineType: "worker",
|
||||
MachineCA: &x509.PEMEncodedCertificateAndKey{
|
||||
Crt: []byte("cert"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
invalidV1alpha1Config := &v1alpha1.Config{}
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
documents []config.Document
|
||||
|
||||
expectedError string
|
||||
expectedWarnings []string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
},
|
||||
{
|
||||
name: "multi-doc",
|
||||
documents: []config.Document{sideroLinkCfg, v1alpha1Cfg},
|
||||
},
|
||||
{
|
||||
name: "only siderolink",
|
||||
documents: []config.Document{sideroLinkCfg},
|
||||
},
|
||||
{
|
||||
name: "only v1alpha1",
|
||||
documents: []config.Document{v1alpha1Cfg},
|
||||
},
|
||||
{
|
||||
name: "invalid siderolink",
|
||||
documents: []config.Document{invalidSideroLinkCfg},
|
||||
expectedError: "1 error occurred:\n\t* SideroLinkConfig: apiUrl is required\n\n",
|
||||
},
|
||||
{
|
||||
name: "invalid v1alpha1",
|
||||
documents: []config.Document{invalidV1alpha1Config},
|
||||
expectedError: "1 error occurred:\n\t* v1alpha1.Config: 1 error occurred:\n\t* machine instructions are required\n\n\n\n",
|
||||
},
|
||||
{
|
||||
name: "invalid multi-doc",
|
||||
documents: []config.Document{invalidSideroLinkCfg, invalidV1alpha1Config},
|
||||
expectedError: "2 errors occurred:\n\t* v1alpha1.Config: 1 error occurred:\n\t* machine instructions are required\n\n\n\t* SideroLinkConfig: apiUrl is required\n\n",
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctr, err := container.New(tt.documents...)
|
||||
require.NoError(t, err)
|
||||
|
||||
warnings, err := ctr.Validate(validationMode{})
|
||||
|
||||
if tt.expectedError == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.EqualError(t, err, tt.expectedError)
|
||||
}
|
||||
|
||||
require.Equal(t, tt.expectedWarnings, warnings)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossValidateEncryption(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
v1alpha1Cfg := &v1alpha1.Config{
|
||||
ClusterConfig: &v1alpha1.ClusterConfig{
|
||||
ControlPlane: &v1alpha1.ControlPlaneConfig{
|
||||
Endpoint: &v1alpha1.Endpoint{
|
||||
URL: must.Value(url.Parse("https://localhost:6443"))(t),
|
||||
},
|
||||
},
|
||||
},
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineType: "worker",
|
||||
MachineCA: &x509.PEMEncodedCertificateAndKey{
|
||||
Crt: []byte("cert"),
|
||||
},
|
||||
MachineSystemDiskEncryption: &v1alpha1.SystemDiskEncryptionConfig{
|
||||
EphemeralPartition: &v1alpha1.EncryptionConfig{
|
||||
EncryptionKeys: []*v1alpha1.EncryptionKey{
|
||||
{
|
||||
KeySlot: 1,
|
||||
KeyStatic: &v1alpha1.EncryptionKeyStatic{
|
||||
KeyData: "static-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
defaultEphemeral := block.NewVolumeConfigV1Alpha1()
|
||||
defaultEphemeral.MetaName = constants.EphemeralPartitionLabel
|
||||
|
||||
encryptedEphemeral := block.NewVolumeConfigV1Alpha1()
|
||||
encryptedEphemeral.MetaName = constants.EphemeralPartitionLabel
|
||||
encryptedEphemeral.EncryptionSpec = block.EncryptionSpec{
|
||||
EncryptionProvider: blockres.EncryptionProviderLUKS2,
|
||||
EncryptionKeys: []block.EncryptionKey{
|
||||
{
|
||||
KeySlot: 2,
|
||||
KeyStatic: &block.EncryptionKeyStatic{
|
||||
KeyData: "encrypted-static-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
encryptedState := block.NewVolumeConfigV1Alpha1()
|
||||
encryptedState.MetaName = constants.StatePartitionLabel
|
||||
encryptedState.EncryptionSpec = block.EncryptionSpec{
|
||||
EncryptionProvider: blockres.EncryptionProviderLUKS2,
|
||||
EncryptionKeys: []block.EncryptionKey{
|
||||
{
|
||||
KeySlot: 3,
|
||||
KeyTPM: &block.EncryptionKeyTPM{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
documents []config.Document
|
||||
|
||||
expectedError string
|
||||
expectedWarnings []string
|
||||
}{
|
||||
{
|
||||
name: "only v1alpha1",
|
||||
documents: []config.Document{v1alpha1Cfg},
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 with no-conflict volumes",
|
||||
documents: []config.Document{v1alpha1Cfg, defaultEphemeral, encryptedState},
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 with no-conflict volumes",
|
||||
documents: []config.Document{v1alpha1Cfg, encryptedState},
|
||||
},
|
||||
{
|
||||
name: "no v1alpha1",
|
||||
documents: []config.Document{encryptedEphemeral, encryptedState},
|
||||
},
|
||||
{
|
||||
name: "conflict on ephemeral encryption",
|
||||
documents: []config.Document{v1alpha1Cfg, encryptedEphemeral},
|
||||
expectedError: "1 error occurred:\n\t* system disk encryption for \"EPHEMERAL\" is configured in both v1alpha1.Config and VolumeConfig\n\n",
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctr, err := container.New(tt.documents...)
|
||||
require.NoError(t, err)
|
||||
|
||||
warnings, err := ctr.Validate(validationMode{})
|
||||
|
||||
if tt.expectedError == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.EqualError(t, err, tt.expectedError)
|
||||
}
|
||||
|
||||
require.Equal(t, tt.expectedWarnings, warnings)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateContainer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sideroLinkCfg := siderolink.NewConfigV1Alpha1()
|
||||
sideroLinkCfg.APIUrlConfig.URL = must.Value(url.Parse("https://siderolink.api/?jointoken=secret&user=alice"))(t)
|
||||
|
||||
v1alpha1Cfg := &v1alpha1.Config{
|
||||
ClusterConfig: &v1alpha1.ClusterConfig{
|
||||
ControlPlane: &v1alpha1.ControlPlaneConfig{
|
||||
Endpoint: &v1alpha1.Endpoint{
|
||||
URL: must.Value(url.Parse("https://localhost:6443"))(t),
|
||||
},
|
||||
},
|
||||
},
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineType: "worker",
|
||||
MachineCA: &x509.PEMEncodedCertificateAndKey{
|
||||
Crt: []byte("cert"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
v1alpha1CfgHostDNS := v1alpha1Cfg.DeepCopy()
|
||||
v1alpha1CfgHostDNS.MachineConfig.MachineFeatures = &v1alpha1.FeaturesConfig{
|
||||
HostDNSSupport: &v1alpha1.HostDNSConfig{ //nolint:staticcheck // testing legacy features
|
||||
HostDNSConfigEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
},
|
||||
}
|
||||
|
||||
resolverConfig := network.NewResolverConfigV1Alpha1()
|
||||
resolverConfig.ResolverNameservers = []network.NameserverConfig{
|
||||
{
|
||||
Address: network.Addr{Addr: netip.MustParseAddr("1.1.1.1")},
|
||||
},
|
||||
}
|
||||
|
||||
hostDNSResolverConfig := network.NewResolverConfigV1Alpha1()
|
||||
hostDNSResolverConfig.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
}
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
documents []config.Document
|
||||
inContainer bool
|
||||
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "empty !container",
|
||||
},
|
||||
{
|
||||
name: "empty container",
|
||||
inContainer: true,
|
||||
|
||||
expectedError: "1 error occurred:\n\t* hostDNS config is required in container mode\n\n",
|
||||
},
|
||||
{
|
||||
name: "empty v1alpha1 container",
|
||||
documents: []config.Document{v1alpha1Cfg},
|
||||
inContainer: true,
|
||||
|
||||
expectedError: "1 error occurred:\n\t* hostDNS config is required in container mode\n\n",
|
||||
},
|
||||
{
|
||||
name: "just resolver in container",
|
||||
documents: []config.Document{resolverConfig},
|
||||
inContainer: true,
|
||||
|
||||
expectedError: "1 error occurred:\n\t* hostDNS config is required in container mode\n\n",
|
||||
},
|
||||
{
|
||||
name: "hostDNS v1alpha1 container",
|
||||
documents: []config.Document{v1alpha1CfgHostDNS},
|
||||
inContainer: true,
|
||||
},
|
||||
{
|
||||
name: "hostDNS v1alpha1 container plus multi-doc",
|
||||
documents: []config.Document{v1alpha1CfgHostDNS, resolverConfig},
|
||||
inContainer: true,
|
||||
},
|
||||
{
|
||||
name: "just multi-doc with hostDNS",
|
||||
documents: []config.Document{hostDNSResolverConfig},
|
||||
inContainer: true,
|
||||
},
|
||||
{
|
||||
name: "multi-doc with hostDNS and v1alpha1",
|
||||
documents: []config.Document{hostDNSResolverConfig, v1alpha1Cfg},
|
||||
inContainer: true,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctr, err := container.New(tt.documents...)
|
||||
require.NoError(t, err)
|
||||
|
||||
warnings, err := ctr.Validate(validationMode{inContainer: tt.inContainer})
|
||||
|
||||
if tt.expectedError == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.EqualError(t, err, tt.expectedError)
|
||||
}
|
||||
|
||||
require.Nil(t, warnings)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type validationMode struct {
|
||||
inContainer bool
|
||||
}
|
||||
|
||||
func (validationMode) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (validationMode) RequiresInstall() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (v validationMode) InContainer() bool {
|
||||
return v.inContainer
|
||||
}
|
||||
@ -209,3 +209,8 @@ func (contract *VersionContract) GrubUseUKICmdlineDefault() bool {
|
||||
func (contract *VersionContract) KubeSpanMultidocConfig() bool {
|
||||
return contract.Greater(TalosVersion1_12)
|
||||
}
|
||||
|
||||
// HostDNSMultidocConfig returns true if version of Talos should use HostDNS multi-doc config.
|
||||
func (contract *VersionContract) HostDNSMultidocConfig() bool {
|
||||
return contract.Greater(TalosVersion1_13)
|
||||
}
|
||||
|
||||
@ -72,6 +72,7 @@ func TestContractCurrent(t *testing.T) {
|
||||
assert.False(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.True(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.True(t, contract.KubeSpanMultidocConfig())
|
||||
assert.True(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_14(t *testing.T) {
|
||||
@ -102,6 +103,7 @@ func TestContract1_14(t *testing.T) {
|
||||
assert.False(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.True(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.True(t, contract.KubeSpanMultidocConfig())
|
||||
assert.True(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_13(t *testing.T) {
|
||||
@ -132,6 +134,7 @@ func TestContract1_13(t *testing.T) {
|
||||
assert.False(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.True(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.True(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_12(t *testing.T) {
|
||||
@ -162,6 +165,7 @@ func TestContract1_12(t *testing.T) {
|
||||
assert.False(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.True(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_11(t *testing.T) {
|
||||
@ -192,6 +196,7 @@ func TestContract1_11(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_10(t *testing.T) {
|
||||
@ -222,6 +227,7 @@ func TestContract1_10(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_9(t *testing.T) {
|
||||
@ -252,6 +258,7 @@ func TestContract1_9(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_8(t *testing.T) {
|
||||
@ -282,6 +289,7 @@ func TestContract1_8(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_7(t *testing.T) {
|
||||
@ -312,6 +320,7 @@ func TestContract1_7(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_6(t *testing.T) {
|
||||
@ -342,6 +351,7 @@ func TestContract1_6(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_5(t *testing.T) {
|
||||
@ -372,6 +382,7 @@ func TestContract1_5(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_4(t *testing.T) {
|
||||
@ -402,6 +413,7 @@ func TestContract1_4(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_3(t *testing.T) {
|
||||
@ -432,6 +444,7 @@ func TestContract1_3(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_2(t *testing.T) {
|
||||
@ -462,6 +475,7 @@ func TestContract1_2(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_1(t *testing.T) {
|
||||
@ -492,6 +506,7 @@ func TestContract1_1(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
func TestContract1_0(t *testing.T) {
|
||||
@ -522,4 +537,5 @@ func TestContract1_0(t *testing.T) {
|
||||
assert.True(t, contract.PopulateClusterSANsFromEndpoint())
|
||||
assert.False(t, contract.GrubUseUKICmdlineDefault())
|
||||
assert.False(t, contract.KubeSpanMultidocConfig())
|
||||
assert.False(t, contract.HostDNSMultidocConfig())
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/siderolabs/gen/xslices"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
@ -18,6 +19,7 @@ import (
|
||||
mc "github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/generate"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/machine"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/cri"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/role"
|
||||
)
|
||||
@ -148,14 +150,18 @@ func TestGenerateRegistryMirrorsOrder(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg, err := input.Config(machine.TypeControlPlane)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
named, ok := cfg.Documents()[1].(mc.NamedDocument)
|
||||
registryConfigs := xslices.Filter(cfg.Documents(), func(doc mc.Document) bool {
|
||||
return doc.Kind() == cri.RegistryMirrorConfig
|
||||
})
|
||||
require.Len(t, registryConfigs, 2)
|
||||
|
||||
named, ok := registryConfigs[0].(mc.NamedDocument)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "a.com", named.Name())
|
||||
|
||||
named, ok = cfg.Documents()[2].(mc.NamedDocument)
|
||||
named, ok = registryConfigs[1].(mc.NamedDocument)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "b.com", named.Name())
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/machine"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/network"
|
||||
v1alpha1 "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
)
|
||||
@ -79,9 +80,9 @@ func (in *Input) init() ([]config.Document, error) {
|
||||
machine.MachineKubelet.KubeletDisableManifestsDirectory = new(true)
|
||||
}
|
||||
|
||||
if in.Options.VersionContract.HostDNSEnabled() {
|
||||
machine.MachineFeatures.HostDNSSupport = &v1alpha1.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
if in.Options.VersionContract.HostDNSEnabled() && !in.Options.VersionContract.HostDNSMultidocConfig() {
|
||||
machine.MachineFeatures.HostDNSSupport = &v1alpha1.HostDNSConfig{ //nolint:staticcheck // legacy configuration
|
||||
HostDNSConfigEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: ptrOrNil(in.Options.HostDNSForwardKubeDNSToHost.ValueOrZero() || in.Options.VersionContract.HostDNSForwardKubeDNSToHost()),
|
||||
}
|
||||
}
|
||||
@ -210,6 +211,16 @@ func (in *Input) init() ([]config.Document, error) {
|
||||
|
||||
documents := []config.Document{v1alpha1Config}
|
||||
|
||||
if in.Options.VersionContract.HostDNSEnabled() && in.Options.VersionContract.HostDNSMultidocConfig() {
|
||||
resolverConfig := network.NewResolverConfigV1Alpha1()
|
||||
resolverConfig.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: ptrOrNil(in.Options.HostDNSForwardKubeDNSToHost.ValueOrZero() || in.Options.VersionContract.HostDNSForwardKubeDNSToHost()),
|
||||
}
|
||||
|
||||
documents = append(documents, resolverConfig)
|
||||
}
|
||||
|
||||
extraDocuments, err := in.generateRegistryConfigs(machine)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate registry configs: %w", err)
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/machine"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/network"
|
||||
v1alpha1 "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
)
|
||||
@ -81,9 +82,9 @@ func (in *Input) worker() ([]config.Document, error) {
|
||||
machine.MachineKubelet.KubeletDisableManifestsDirectory = new(true)
|
||||
}
|
||||
|
||||
if in.Options.VersionContract.HostDNSEnabled() {
|
||||
machine.MachineFeatures.HostDNSSupport = &v1alpha1.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
if in.Options.VersionContract.HostDNSEnabled() && !in.Options.VersionContract.HostDNSMultidocConfig() {
|
||||
machine.MachineFeatures.HostDNSSupport = &v1alpha1.HostDNSConfig{ //nolint:staticcheck // legacy config
|
||||
HostDNSConfigEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: ptrOrNil(in.Options.HostDNSForwardKubeDNSToHost.ValueOrZero() || in.Options.VersionContract.HostDNSForwardKubeDNSToHost()),
|
||||
}
|
||||
}
|
||||
@ -143,6 +144,16 @@ func (in *Input) worker() ([]config.Document, error) {
|
||||
|
||||
documents := []config.Document{v1alpha1Config}
|
||||
|
||||
if in.Options.VersionContract.HostDNSEnabled() && in.Options.VersionContract.HostDNSMultidocConfig() {
|
||||
resolverConfig := network.NewResolverConfigV1Alpha1()
|
||||
resolverConfig.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: ptrOrNil(in.Options.HostDNSForwardKubeDNSToHost.ValueOrZero() || in.Options.VersionContract.HostDNSForwardKubeDNSToHost()),
|
||||
}
|
||||
|
||||
documents = append(documents, resolverConfig)
|
||||
}
|
||||
|
||||
extraDocuments, err := in.generateRegistryConfigs(machine)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate registry configs: %w", err)
|
||||
|
||||
@ -2226,6 +2226,34 @@
|
||||
],
|
||||
"description": "HCloudVIPConfig is a config document to configure virtual IP using Hetzner Cloud APIs for announcement.\\nVirtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server.\\nAny other use cases are not supported and may lead to unexpected behavior.\\nVirtual IP will be announced from only one node at a time using Hetzner Cloud APIs.\\n"
|
||||
},
|
||||
"network.HostDNSConfig": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"title": "enabled",
|
||||
"description": "Enable host DNS caching resolver.\n\nWhen enabled, a local DNS caching resolver is deployed on the host to improve DNS resolution performance and reliability.\nUpstream DNS servers for the host resolver are configured using the nameservers field in this config document.\n",
|
||||
"markdownDescription": "Enable host DNS caching resolver.\n\nWhen enabled, a local DNS caching resolver is deployed on the host to improve DNS resolution performance and reliability.\nUpstream DNS servers for the host resolver are configured using the `nameservers` field in this config document.",
|
||||
"x-intellij-html-description": "\u003cp\u003eEnable host DNS caching resolver.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, a local DNS caching resolver is deployed on the host to improve DNS resolution performance and reliability.\nUpstream DNS servers for the host resolver are configured using the \u003ccode\u003enameservers\u003c/code\u003e field in this config document.\u003c/p\u003e\n"
|
||||
},
|
||||
"forwardKubeDNSToHost": {
|
||||
"type": "boolean",
|
||||
"title": "forwardKubeDNSToHost",
|
||||
"description": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\n",
|
||||
"markdownDescription": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).",
|
||||
"x-intellij-html-description": "\u003cp\u003eUse the host DNS resolver as upstream for Kubernetes CoreDNS pods.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\u003c/p\u003e\n"
|
||||
},
|
||||
"resolveMemberNames": {
|
||||
"type": "boolean",
|
||||
"title": "resolveMemberNames",
|
||||
"description": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\n",
|
||||
"markdownDescription": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.",
|
||||
"x-intellij-html-description": "\u003cp\u003eResolve member hostnames using the host DNS resolver.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "HostDNSConfig represents host DNS configuration."
|
||||
},
|
||||
"network.HostnameConfigV1Alpha1": {
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
@ -2705,6 +2733,13 @@
|
||||
"description": "Configuration for search domains (in /etc/resolv.conf).\n\nThe default is to derive search domains from the hostname FQDN.\n",
|
||||
"markdownDescription": "Configuration for search domains (in /etc/resolv.conf).\n\nThe default is to derive search domains from the hostname FQDN.",
|
||||
"x-intellij-html-description": "\u003cp\u003eConfiguration for search domains (in /etc/resolv.conf).\u003c/p\u003e\n\n\u003cp\u003eThe default is to derive search domains from the hostname FQDN.\u003c/p\u003e\n"
|
||||
},
|
||||
"hostDNS": {
|
||||
"$ref": "#/$defs/network.HostDNSConfig",
|
||||
"title": "hostDNS",
|
||||
"description": "Configuration for host DNS resolver.\n\nThis configures a local DNS caching resolver on the host to improve DNS resolution performance and reliability.\n",
|
||||
"markdownDescription": "Configuration for host DNS resolver.\n\nThis configures a local DNS caching resolver on the host to improve DNS resolution performance and reliability.",
|
||||
"x-intellij-html-description": "\u003cp\u003eConfiguration for host DNS resolver.\u003c/p\u003e\n\n\u003cp\u003eThis configures a local DNS caching resolver on the host to improve DNS resolution performance and reliability.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@ -4674,13 +4709,6 @@
|
||||
"markdownDescription": "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.",
|
||||
"x-intellij-html-description": "\u003cp\u003eKubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\u003c/p\u003e\n"
|
||||
},
|
||||
"hostDNS": {
|
||||
"$ref": "#/$defs/v1alpha1.HostDNSConfig",
|
||||
"title": "hostDNS",
|
||||
"description": "Configures host DNS caching resolver.\n",
|
||||
"markdownDescription": "Configures host DNS caching resolver.",
|
||||
"x-intellij-html-description": "\u003cp\u003eConfigures host DNS caching resolver.\u003c/p\u003e\n"
|
||||
},
|
||||
"imageCache": {
|
||||
"$ref": "#/$defs/v1alpha1.ImageCacheConfig",
|
||||
"title": "imageCache",
|
||||
@ -4724,34 +4752,6 @@
|
||||
"type": "object",
|
||||
"description": "FlannelCNIConfig represents the Flannel CNI configuration options."
|
||||
},
|
||||
"v1alpha1.HostDNSConfig": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"title": "enabled",
|
||||
"description": "Enable host DNS caching resolver.\n",
|
||||
"markdownDescription": "Enable host DNS caching resolver.",
|
||||
"x-intellij-html-description": "\u003cp\u003eEnable host DNS caching resolver.\u003c/p\u003e\n"
|
||||
},
|
||||
"forwardKubeDNSToHost": {
|
||||
"type": "boolean",
|
||||
"title": "forwardKubeDNSToHost",
|
||||
"description": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\n",
|
||||
"markdownDescription": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).",
|
||||
"x-intellij-html-description": "\u003cp\u003eUse the host DNS resolver as upstream for Kubernetes CoreDNS pods.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\u003c/p\u003e\n"
|
||||
},
|
||||
"resolveMemberNames": {
|
||||
"type": "boolean",
|
||||
"title": "resolveMemberNames",
|
||||
"description": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\n",
|
||||
"markdownDescription": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.",
|
||||
"x-intellij-html-description": "\u003cp\u003eResolve member hostnames using the host DNS resolver.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "HostDNSConfig describes the configuration for the host DNS resolver."
|
||||
},
|
||||
"v1alpha1.ImageCacheConfig": {
|
||||
"properties": {
|
||||
"localEnabled": {
|
||||
|
||||
@ -515,6 +515,18 @@ func (o *ResolverConfigV1Alpha1) DeepCopy() *ResolverConfigV1Alpha1 {
|
||||
cp.ResolverSearchDomains.SearchDisableDefault = new(bool)
|
||||
*cp.ResolverSearchDomains.SearchDisableDefault = *o.ResolverSearchDomains.SearchDisableDefault
|
||||
}
|
||||
if o.ResolverHostDNS.HostDNSEnabled != nil {
|
||||
cp.ResolverHostDNS.HostDNSEnabled = new(bool)
|
||||
*cp.ResolverHostDNS.HostDNSEnabled = *o.ResolverHostDNS.HostDNSEnabled
|
||||
}
|
||||
if o.ResolverHostDNS.HostDNSForwardKubeDNSToHost != nil {
|
||||
cp.ResolverHostDNS.HostDNSForwardKubeDNSToHost = new(bool)
|
||||
*cp.ResolverHostDNS.HostDNSForwardKubeDNSToHost = *o.ResolverHostDNS.HostDNSForwardKubeDNSToHost
|
||||
}
|
||||
if o.ResolverHostDNS.HostDNSResolveMemberNames != nil {
|
||||
cp.ResolverHostDNS.HostDNSResolveMemberNames = new(bool)
|
||||
*cp.ResolverHostDNS.HostDNSResolveMemberNames = *o.ResolverHostDNS.HostDNSResolveMemberNames
|
||||
}
|
||||
return &cp
|
||||
}
|
||||
|
||||
|
||||
@ -1450,6 +1450,13 @@ func (ResolverConfigV1Alpha1) Doc() *encoder.Doc {
|
||||
Description: "Configuration for search domains (in /etc/resolv.conf).\n\nThe default is to derive search domains from the hostname FQDN.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Configuration for search domains (in /etc/resolv.conf)." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "hostDNS",
|
||||
Type: "HostDNSConfig",
|
||||
Note: "",
|
||||
Description: "Configuration for host DNS resolver.\n\nThis configures a local DNS caching resolver on the host to improve DNS resolution performance and reliability.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Configuration for host DNS resolver." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -1457,6 +1464,8 @@ func (ResolverConfigV1Alpha1) Doc() *encoder.Doc {
|
||||
|
||||
doc.AddExample("", exampleResolverConfigV1Alpha2())
|
||||
|
||||
doc.AddExample("", exampleResolverConfigV1Alpha3())
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
@ -1519,6 +1528,45 @@ func (SearchDomainsConfig) Doc() *encoder.Doc {
|
||||
return doc
|
||||
}
|
||||
|
||||
func (HostDNSConfig) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "HostDNSConfig",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "HostDNSConfig represents host DNS configuration." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "HostDNSConfig represents host DNS configuration.",
|
||||
AppearsIn: []encoder.Appearance{
|
||||
{
|
||||
TypeName: "ResolverConfigV1Alpha1",
|
||||
FieldName: "hostDNS",
|
||||
},
|
||||
},
|
||||
Fields: []encoder.Doc{
|
||||
{
|
||||
Name: "enabled",
|
||||
Type: "bool",
|
||||
Note: "",
|
||||
Description: "Enable host DNS caching resolver.\n\nWhen enabled, a local DNS caching resolver is deployed on the host to improve DNS resolution performance and reliability.\nUpstream DNS servers for the host resolver are configured using the `nameservers` field in this config document.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Enable host DNS caching resolver." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "forwardKubeDNSToHost",
|
||||
Type: "bool",
|
||||
Note: "",
|
||||
Description: "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "resolveMemberNames",
|
||||
Type: "bool",
|
||||
Note: "",
|
||||
Description: "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Resolve member hostnames using the host DNS resolver." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func (RoutingRuleConfigV1Alpha1) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "RoutingRuleConfig",
|
||||
@ -2155,6 +2203,7 @@ func GetFileDoc() *encoder.FileDoc {
|
||||
ResolverConfigV1Alpha1{}.Doc(),
|
||||
NameserverConfig{}.Doc(),
|
||||
SearchDomainsConfig{}.Doc(),
|
||||
HostDNSConfig{}.Doc(),
|
||||
RoutingRuleConfigV1Alpha1{}.Doc(),
|
||||
RuleConfigV1Alpha1{}.Doc(),
|
||||
RulePortSelector{}.Doc(),
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"net/netip"
|
||||
"slices"
|
||||
|
||||
"github.com/siderolabs/gen/value"
|
||||
"github.com/siderolabs/gen/xslices"
|
||||
"github.com/siderolabs/go-pointer"
|
||||
|
||||
@ -19,6 +20,7 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/internal/registry"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/validation"
|
||||
)
|
||||
|
||||
// ResolverKind is a ResolverConfig document kind.
|
||||
@ -38,6 +40,8 @@ func init() {
|
||||
// Check interfaces.
|
||||
var (
|
||||
_ config.NetworkResolverConfig = &ResolverConfigV1Alpha1{}
|
||||
_ config.NetworkHostDNSConfig = &ResolverConfigV1Alpha1{}
|
||||
_ config.Validator = &ResolverConfigV1Alpha1{}
|
||||
_ container.V1Alpha1ConflictValidator = &ResolverConfigV1Alpha1{}
|
||||
)
|
||||
|
||||
@ -46,6 +50,7 @@ var (
|
||||
// examples:
|
||||
// - value: exampleResolverConfigV1Alpha1()
|
||||
// - value: exampleResolverConfigV1Alpha2()
|
||||
// - value: exampleResolverConfigV1Alpha3()
|
||||
// alias: ResolverConfig
|
||||
// schemaRoot: true
|
||||
// schemaMeta: v1alpha1/ResolverConfig
|
||||
@ -66,6 +71,11 @@ type ResolverConfigV1Alpha1 struct {
|
||||
//
|
||||
// The default is to derive search domains from the hostname FQDN.
|
||||
ResolverSearchDomains SearchDomainsConfig `yaml:"searchDomains,omitempty"`
|
||||
// description: |
|
||||
// Configuration for host DNS resolver.
|
||||
//
|
||||
// This configures a local DNS caching resolver on the host to improve DNS resolution performance and reliability.
|
||||
ResolverHostDNS HostDNSConfig `yaml:"hostDNS,omitempty"`
|
||||
}
|
||||
|
||||
// NameserverConfig represents a single nameserver configuration.
|
||||
@ -101,6 +111,28 @@ type SearchDomainsConfig struct {
|
||||
SearchDisableDefault *bool `yaml:"disableDefault,omitempty"`
|
||||
}
|
||||
|
||||
// HostDNSConfig represents host DNS configuration.
|
||||
type HostDNSConfig struct {
|
||||
// description: |
|
||||
// Enable host DNS caching resolver.
|
||||
//
|
||||
// When enabled, a local DNS caching resolver is deployed on the host to improve DNS resolution performance and reliability.
|
||||
// Upstream DNS servers for the host resolver are configured using the `nameservers` field in this config document.
|
||||
HostDNSEnabled *bool `yaml:"enabled,omitempty"`
|
||||
// description: |
|
||||
// Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.
|
||||
//
|
||||
// When enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of
|
||||
// using configured upstream DNS resolvers directly).
|
||||
HostDNSForwardKubeDNSToHost *bool `yaml:"forwardKubeDNSToHost,omitempty"`
|
||||
// description: |
|
||||
// Resolve member hostnames using the host DNS resolver.
|
||||
//
|
||||
// When enabled, cluster member hostnames and node names are resolved using the host DNS resolver.
|
||||
// This requires service discovery to be enabled.
|
||||
HostDNSResolveMemberNames *bool `yaml:"resolveMemberNames,omitempty"`
|
||||
}
|
||||
|
||||
// NewResolverConfigV1Alpha1 creates a new ResolverConfig config document.
|
||||
func NewResolverConfigV1Alpha1() *ResolverConfigV1Alpha1 {
|
||||
return &ResolverConfigV1Alpha1{
|
||||
@ -137,6 +169,17 @@ func exampleResolverConfigV1Alpha2() *ResolverConfigV1Alpha1 {
|
||||
return cfg
|
||||
}
|
||||
|
||||
func exampleResolverConfigV1Alpha3() *ResolverConfigV1Alpha1 {
|
||||
cfg := NewResolverConfigV1Alpha1()
|
||||
cfg.ResolverHostDNS = HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
HostDNSResolveMemberNames: new(true),
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Clone implements config.Document interface.
|
||||
func (s *ResolverConfigV1Alpha1) Clone() config.Document {
|
||||
return s.DeepCopy()
|
||||
@ -156,9 +199,34 @@ func (s *ResolverConfigV1Alpha1) V1Alpha1ConflictValidate(v1alpha1Cfg *v1alpha1.
|
||||
return errors.New(".machine.network.disableSearchDomain is already set in v1alpha1 config")
|
||||
}
|
||||
|
||||
if !value.IsZero(s.ResolverHostDNS) {
|
||||
if v1alpha1Cfg.NetworkHostDNSConfig() != nil {
|
||||
return errors.New(".machine.features.hostDNS is already set in v1alpha1 config")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate implements config.Validator interface.
|
||||
func (s *ResolverConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) {
|
||||
var errs error
|
||||
|
||||
if !value.IsZero(s.ResolverHostDNS) {
|
||||
if !s.HostDNSEnabled() {
|
||||
if s.ForwardKubeDNSToHost() {
|
||||
errs = errors.Join(errs, errors.New("hostDNS.forwardKubeDNSToHost cannot be enabled when hostDNS.enabled is false"))
|
||||
}
|
||||
|
||||
if s.ResolveMemberNames() {
|
||||
errs = errors.Join(errs, errors.New("hostDNS.resolveMemberNames cannot be enabled when hostDNS.enabled is false"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
// Resolvers implements NetworkResolverConfig interface.
|
||||
func (s *ResolverConfigV1Alpha1) Resolvers() []netip.Addr {
|
||||
return xslices.Map(s.ResolverNameservers, func(ns NameserverConfig) netip.Addr {
|
||||
@ -175,3 +243,18 @@ func (s *ResolverConfigV1Alpha1) SearchDomains() []string {
|
||||
func (s *ResolverConfigV1Alpha1) DisableSearchDomain() bool {
|
||||
return pointer.SafeDeref(s.ResolverSearchDomains.SearchDisableDefault)
|
||||
}
|
||||
|
||||
// HostDNSEnabled implements NetworkHostDNSConfig interface.
|
||||
func (s *ResolverConfigV1Alpha1) HostDNSEnabled() bool {
|
||||
return pointer.SafeDeref(s.ResolverHostDNS.HostDNSEnabled)
|
||||
}
|
||||
|
||||
// ForwardKubeDNSToHost implements NetworkHostDNSConfig interface.
|
||||
func (s *ResolverConfigV1Alpha1) ForwardKubeDNSToHost() bool {
|
||||
return pointer.SafeDeref(s.ResolverHostDNS.HostDNSForwardKubeDNSToHost)
|
||||
}
|
||||
|
||||
// ResolveMemberNames implements NetworkHostDNSConfig interface.
|
||||
func (s *ResolverConfigV1Alpha1) ResolveMemberNames() bool {
|
||||
return pointer.SafeDeref(s.ResolverHostDNS.HostDNSResolveMemberNames)
|
||||
}
|
||||
|
||||
@ -22,6 +22,9 @@ import (
|
||||
//go:embed testdata/resolverconfig.yaml
|
||||
var expectedResolverConfigDocument []byte
|
||||
|
||||
//go:embed testdata/resolverconfig_with_hostdns.yaml
|
||||
var expectedResolverConfigDocumentWithHostDNS []byte
|
||||
|
||||
func TestResolverConfigMarshalStability(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@ -47,6 +50,29 @@ func TestResolverConfigMarshalStability(t *testing.T) {
|
||||
assert.Equal(t, expectedResolverConfigDocument, marshaled)
|
||||
}
|
||||
|
||||
func TestResolverConfigMarshalStabilityWithHostDNS(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := network.NewResolverConfigV1Alpha1()
|
||||
cfg.ResolverNameservers = []network.NameserverConfig{
|
||||
{
|
||||
Address: network.Addr{Addr: netip.MustParseAddr("10.0.0.1")},
|
||||
},
|
||||
}
|
||||
cfg.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
HostDNSResolveMemberNames: new(false),
|
||||
}
|
||||
|
||||
marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Log(string(marshaled))
|
||||
|
||||
assert.Equal(t, expectedResolverConfigDocumentWithHostDNS, marshaled)
|
||||
}
|
||||
|
||||
func TestResolverConfigUnmarshal(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@ -76,7 +102,7 @@ func TestResolverConfigUnmarshal(t *testing.T) {
|
||||
}, docs[0])
|
||||
}
|
||||
|
||||
func TestResolverV1Alpha1Validate(t *testing.T) {
|
||||
func TestResolverV1Alpha1ConflictValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, test := range []struct {
|
||||
@ -130,6 +156,57 @@ func TestResolverV1Alpha1Validate(t *testing.T) {
|
||||
|
||||
expectedError: ".machine.network.disableSearchDomain is already set in v1alpha1 config",
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 hostDNS and resolver hostDNS set",
|
||||
v1alpha1Cfg: &v1alpha1.Config{
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineFeatures: &v1alpha1.FeaturesConfig{
|
||||
HostDNSSupport: &v1alpha1.HostDNSConfig{ //nolint:staticcheck // testing legacy features
|
||||
HostDNSConfigEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
cfg: func() *network.ResolverConfigV1Alpha1 {
|
||||
cfg := network.NewResolverConfigV1Alpha1()
|
||||
cfg.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
}
|
||||
|
||||
return cfg
|
||||
},
|
||||
|
||||
expectedError: ".machine.features.hostDNS is already set in v1alpha1 config",
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 hostDNS and no resolver hostDNS set",
|
||||
v1alpha1Cfg: &v1alpha1.Config{
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineFeatures: &v1alpha1.FeaturesConfig{
|
||||
HostDNSSupport: &v1alpha1.HostDNSConfig{ //nolint:staticcheck // testing legacy features
|
||||
HostDNSConfigEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
cfg: network.NewResolverConfigV1Alpha1,
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 no hostDNS and resolver hostDNS set",
|
||||
v1alpha1Cfg: &v1alpha1.Config{},
|
||||
cfg: func() *network.ResolverConfigV1Alpha1 {
|
||||
cfg := network.NewResolverConfigV1Alpha1()
|
||||
cfg.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
}
|
||||
|
||||
return cfg
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -143,3 +220,73 @@ func TestResolverV1Alpha1Validate(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolverV1Alpha1Validate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
cfg func() *network.ResolverConfigV1Alpha1
|
||||
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
cfg: network.NewResolverConfigV1Alpha1,
|
||||
},
|
||||
{
|
||||
name: "forwardKubeDNSToHost true but HostDNSEnabled false",
|
||||
cfg: func() *network.ResolverConfigV1Alpha1 {
|
||||
cfg := network.NewResolverConfigV1Alpha1()
|
||||
cfg.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(false),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
}
|
||||
|
||||
return cfg
|
||||
},
|
||||
|
||||
expectedError: "hostDNS.forwardKubeDNSToHost cannot be enabled when hostDNS.enabled is false",
|
||||
},
|
||||
{
|
||||
name: "resolveMemberNames true but HostDNSEnabled false",
|
||||
cfg: func() *network.ResolverConfigV1Alpha1 {
|
||||
cfg := network.NewResolverConfigV1Alpha1()
|
||||
cfg.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(false),
|
||||
HostDNSResolveMemberNames: new(true),
|
||||
}
|
||||
|
||||
return cfg
|
||||
},
|
||||
|
||||
expectedError: "hostDNS.resolveMemberNames cannot be enabled when hostDNS.enabled is false",
|
||||
},
|
||||
{
|
||||
name: "hostDNS config valid",
|
||||
cfg: func() *network.ResolverConfigV1Alpha1 {
|
||||
cfg := network.NewResolverConfigV1Alpha1()
|
||||
cfg.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
HostDNSResolveMemberNames: new(false),
|
||||
}
|
||||
|
||||
return cfg
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
warnings, err := test.cfg().Validate(validationMode{})
|
||||
assert.Nil(t, warnings)
|
||||
|
||||
if test.expectedError != "" {
|
||||
assert.EqualError(t, err, test.expectedError)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
8
pkg/machinery/config/types/network/testdata/resolverconfig_with_hostdns.yaml
vendored
Normal file
8
pkg/machinery/config/types/network/testdata/resolverconfig_with_hostdns.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
apiVersion: v1alpha1
|
||||
kind: ResolverConfig
|
||||
nameservers:
|
||||
- address: 10.0.0.1
|
||||
hostDNS:
|
||||
enabled: true
|
||||
forwardKubeDNSToHost: true
|
||||
resolveMemberNames: false
|
||||
@ -20,9 +20,6 @@ machine:
|
||||
kubePrism:
|
||||
enabled: true
|
||||
port: 7445
|
||||
hostDNS:
|
||||
enabled: true
|
||||
forwardKubeDNSToHost: true
|
||||
nodeLabels:
|
||||
node.kubernetes.io/exclude-from-external-load-balancers: ""
|
||||
cluster:
|
||||
@ -89,5 +86,11 @@ cluster:
|
||||
key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: ResolverConfig
|
||||
hostDNS:
|
||||
enabled: true
|
||||
forwardKubeDNSToHost: true
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: HostnameConfig
|
||||
auto: stable
|
||||
|
||||
@ -20,9 +20,6 @@ machine:
|
||||
kubePrism:
|
||||
enabled: true
|
||||
port: 7445
|
||||
hostDNS:
|
||||
enabled: true
|
||||
forwardKubeDNSToHost: true
|
||||
cluster:
|
||||
id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w=
|
||||
secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic=
|
||||
@ -47,5 +44,11 @@ cluster:
|
||||
service: {}
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: ResolverConfig
|
||||
hostDNS:
|
||||
enabled: true
|
||||
forwardKubeDNSToHost: true
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: HostnameConfig
|
||||
auto: stable
|
||||
|
||||
@ -34,9 +34,6 @@ machine:
|
||||
kubePrism:
|
||||
enabled: true
|
||||
port: 7445
|
||||
hostDNS:
|
||||
enabled: true
|
||||
forwardKubeDNSToHost: true
|
||||
nodeLabels:
|
||||
node.kubernetes.io/exclude-from-external-load-balancers: ""
|
||||
cluster:
|
||||
@ -112,6 +109,12 @@ cluster:
|
||||
allowSchedulingOnControlPlanes: true
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: ResolverConfig
|
||||
hostDNS:
|
||||
enabled: true
|
||||
forwardKubeDNSToHost: true
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: RegistryMirrorConfig
|
||||
name: ghcr.io
|
||||
endpoints:
|
||||
|
||||
@ -34,9 +34,6 @@ machine:
|
||||
kubePrism:
|
||||
enabled: true
|
||||
port: 7445
|
||||
hostDNS:
|
||||
enabled: true
|
||||
forwardKubeDNSToHost: true
|
||||
cluster:
|
||||
id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w=
|
||||
secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic=
|
||||
@ -65,6 +62,12 @@ cluster:
|
||||
service: {}
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: ResolverConfig
|
||||
hostDNS:
|
||||
enabled: true
|
||||
forwardKubeDNSToHost: true
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: RegistryMirrorConfig
|
||||
name: ghcr.io
|
||||
endpoints:
|
||||
|
||||
@ -21,15 +21,6 @@ func (f *FeaturesConfig) DiskQuotaSupportEnabled() bool {
|
||||
return pointer.SafeDeref(f.DiskQuotaSupport)
|
||||
}
|
||||
|
||||
// HostDNS implements config.Features interface.
|
||||
func (f *FeaturesConfig) HostDNS() config.HostDNS {
|
||||
if f.HostDNSSupport == nil {
|
||||
return &HostDNSConfig{}
|
||||
}
|
||||
|
||||
return f.HostDNSSupport
|
||||
}
|
||||
|
||||
// KubePrism implements config.Features interface.
|
||||
func (f *FeaturesConfig) KubePrism() config.KubePrism {
|
||||
if f.KubePrismSupport == nil {
|
||||
@ -78,17 +69,17 @@ func (a *KubePrism) Port() int {
|
||||
return a.ServerPort
|
||||
}
|
||||
|
||||
// Enabled implements config.HostDNS.
|
||||
func (h *HostDNSConfig) Enabled() bool {
|
||||
return pointer.SafeDeref(h.HostDNSEnabled)
|
||||
// HostDNSEnabled implements config.NetworkHostDNSConfig interface.
|
||||
func (h *HostDNSConfig) HostDNSEnabled() bool {
|
||||
return pointer.SafeDeref(h.HostDNSConfigEnabled)
|
||||
}
|
||||
|
||||
// ForwardKubeDNSToHost implements config.HostDNS.
|
||||
// ForwardKubeDNSToHost implements config.NetworkHostDNSConfig interface.
|
||||
func (h *HostDNSConfig) ForwardKubeDNSToHost() bool {
|
||||
return pointer.SafeDeref(h.HostDNSForwardKubeDNSToHost)
|
||||
}
|
||||
|
||||
// ResolveMemberNames implements config.HostDNS.
|
||||
// ResolveMemberNames implements config.NetworkHostDNSConfig interface.
|
||||
func (h *HostDNSConfig) ResolveMemberNames() bool {
|
||||
return pointer.SafeDeref(h.HostDNSResolveMemberNames)
|
||||
}
|
||||
|
||||
@ -99,3 +99,12 @@ func (c *Config) NetworkKubeSpanConfig() config.NetworkKubeSpanConfig {
|
||||
|
||||
return c.MachineConfig.MachineNetwork.NetworkKubeSpan
|
||||
}
|
||||
|
||||
// NetworkHostDNSConfig implements the config.NetworkHostDNSConfig interface.
|
||||
func (c *Config) NetworkHostDNSConfig() config.NetworkHostDNSConfig {
|
||||
if c.MachineConfig == nil || c.MachineConfig.MachineFeatures == nil || c.MachineConfig.MachineFeatures.HostDNSSupport == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.MachineConfig.MachineFeatures.HostDNSSupport
|
||||
}
|
||||
|
||||
@ -556,3 +556,141 @@ func TestKubeSpanBridging(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostDNSConfigBridging(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
|
||||
cfg func(*testing.T) config.Config
|
||||
|
||||
expectedHostDNSConfigExists bool
|
||||
expectedHostDNSEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "v1alpha1 empty",
|
||||
|
||||
cfg: func(*testing.T) config.Config {
|
||||
return container.NewV1Alpha1(&v1alpha1.Config{
|
||||
MachineConfig: &v1alpha1.MachineConfig{},
|
||||
})
|
||||
},
|
||||
|
||||
expectedHostDNSConfigExists: false,
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 hostDNS enabled",
|
||||
|
||||
cfg: func(*testing.T) config.Config {
|
||||
return container.NewV1Alpha1(&v1alpha1.Config{
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineFeatures: &v1alpha1.FeaturesConfig{
|
||||
HostDNSSupport: &v1alpha1.HostDNSConfig{ //nolint:staticcheck // testing legacy features
|
||||
HostDNSConfigEnabled: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
expectedHostDNSConfigExists: true,
|
||||
expectedHostDNSEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "resolver config hostDNS enabled",
|
||||
|
||||
cfg: func(*testing.T) config.Config {
|
||||
resolverCfg := network.NewResolverConfigV1Alpha1()
|
||||
resolverCfg.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
HostDNSResolveMemberNames: new(true),
|
||||
}
|
||||
|
||||
c, err := container.New(
|
||||
resolverCfg,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return c
|
||||
},
|
||||
|
||||
expectedHostDNSConfigExists: true,
|
||||
expectedHostDNSEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 empty and resolver config hostDNS enabled",
|
||||
|
||||
cfg: func(*testing.T) config.Config {
|
||||
v1alpha1Cfg := &v1alpha1.Config{
|
||||
MachineConfig: &v1alpha1.MachineConfig{},
|
||||
}
|
||||
|
||||
resolverCfg := network.NewResolverConfigV1Alpha1()
|
||||
resolverCfg.ResolverHostDNS = network.HostDNSConfig{
|
||||
HostDNSEnabled: new(true),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
HostDNSResolveMemberNames: new(true),
|
||||
}
|
||||
|
||||
c, err := container.New(
|
||||
v1alpha1Cfg,
|
||||
resolverCfg,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return c
|
||||
},
|
||||
|
||||
expectedHostDNSConfigExists: true,
|
||||
expectedHostDNSEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 hostDNS enabled and resolver empty",
|
||||
|
||||
cfg: func(*testing.T) config.Config {
|
||||
v1alpha1Cfg := &v1alpha1.Config{
|
||||
MachineConfig: &v1alpha1.MachineConfig{
|
||||
MachineFeatures: &v1alpha1.FeaturesConfig{
|
||||
HostDNSSupport: &v1alpha1.HostDNSConfig{ //nolint:staticcheck // testing legacy features
|
||||
HostDNSConfigEnabled: new(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resolverCfg := network.NewResolverConfigV1Alpha1()
|
||||
|
||||
c, err := container.New(
|
||||
v1alpha1Cfg,
|
||||
resolverCfg,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return c
|
||||
},
|
||||
|
||||
expectedHostDNSConfigExists: true,
|
||||
expectedHostDNSEnabled: true,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := test.cfg(t)
|
||||
|
||||
hostDNSConfig := cfg.NetworkHostDNSConfig()
|
||||
|
||||
if !test.expectedHostDNSConfigExists {
|
||||
require.Nil(t, hostDNSConfig)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.NotNil(t, hostDNSConfig)
|
||||
|
||||
assert.Equal(t, test.expectedHostDNSEnabled, hostDNSConfig.HostDNSEnabled())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -2394,8 +2394,9 @@ type FeaturesConfig struct {
|
||||
// KubePrism - local proxy/load balancer on defined port that will distribute
|
||||
// requests to all API servers in the cluster.
|
||||
KubePrismSupport *KubePrism `yaml:"kubePrism,omitempty"`
|
||||
// description: |
|
||||
// Configures host DNS caching resolver.
|
||||
// docgen:nodoc
|
||||
//
|
||||
// Deprecated: Use ResolverConfig document instead.
|
||||
HostDNSSupport *HostDNSConfig `yaml:"hostDNS,omitempty"`
|
||||
// description: |
|
||||
// Enable Image Cache feature.
|
||||
@ -2441,10 +2442,14 @@ type KubernetesTalosAPIAccessConfig struct {
|
||||
}
|
||||
|
||||
// HostDNSConfig describes the configuration for the host DNS resolver.
|
||||
//
|
||||
// Deprecated: Use ResolverConfig document instead.
|
||||
//
|
||||
// docgen:nodoc
|
||||
type HostDNSConfig struct {
|
||||
// description: |
|
||||
// Enable host DNS caching resolver.
|
||||
HostDNSEnabled *bool `yaml:"enabled,omitempty"`
|
||||
HostDNSConfigEnabled *bool `yaml:"enabled,omitempty"`
|
||||
// description: |
|
||||
// Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.
|
||||
//
|
||||
|
||||
@ -1880,13 +1880,7 @@ func (FeaturesConfig) Doc() *encoder.Doc {
|
||||
Description: "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "KubePrism - local proxy/load balancer on defined port that will distribute" /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "hostDNS",
|
||||
Type: "HostDNSConfig",
|
||||
Note: "",
|
||||
Description: "Configures host DNS caching resolver.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Configures host DNS caching resolver." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{},
|
||||
{
|
||||
Name: "imageCache",
|
||||
Type: "ImageCacheConfig",
|
||||
@ -2009,45 +2003,6 @@ func (KubernetesTalosAPIAccessConfig) Doc() *encoder.Doc {
|
||||
return doc
|
||||
}
|
||||
|
||||
func (HostDNSConfig) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "HostDNSConfig",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "HostDNSConfig describes the configuration for the host DNS resolver." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "HostDNSConfig describes the configuration for the host DNS resolver.",
|
||||
AppearsIn: []encoder.Appearance{
|
||||
{
|
||||
TypeName: "FeaturesConfig",
|
||||
FieldName: "hostDNS",
|
||||
},
|
||||
},
|
||||
Fields: []encoder.Doc{
|
||||
{
|
||||
Name: "enabled",
|
||||
Type: "bool",
|
||||
Note: "",
|
||||
Description: "Enable host DNS caching resolver.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Enable host DNS caching resolver." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "forwardKubeDNSToHost",
|
||||
Type: "bool",
|
||||
Note: "",
|
||||
Description: "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "resolveMemberNames",
|
||||
Type: "bool",
|
||||
Note: "",
|
||||
Description: "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Resolve member hostnames using the host DNS resolver." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func (VolumeMountConfig) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "VolumeMountConfig",
|
||||
@ -2459,7 +2414,6 @@ func GetFileDoc() *encoder.FileDoc {
|
||||
KubePrism{}.Doc(),
|
||||
ImageCacheConfig{}.Doc(),
|
||||
KubernetesTalosAPIAccessConfig{}.Doc(),
|
||||
HostDNSConfig{}.Doc(),
|
||||
VolumeMountConfig{}.Doc(),
|
||||
ClusterInlineManifest{}.Doc(),
|
||||
ClusterDiscoveryConfig{}.Doc(),
|
||||
|
||||
@ -117,19 +117,10 @@ func (c *Config) Validate(mode validation.RuntimeMode, options ...validation.Opt
|
||||
}
|
||||
}
|
||||
|
||||
if mode.InContainer() {
|
||||
// require that HostDNS features are enabled to passthrough container DNS to kube-dns
|
||||
if !c.Machine().Features().HostDNS().Enabled() {
|
||||
result = multierror.Append(result, errors.New("feature HostDNS should be enabled in container mode (.machine.features.hostDNS.enabled)"))
|
||||
if c.NetworkHostDNSConfig() != nil {
|
||||
if c.NetworkHostDNSConfig().ForwardKubeDNSToHost() && !c.NetworkHostDNSConfig().HostDNSEnabled() {
|
||||
result = multierror.Append(result, errors.New("feature hostDNS.forwardKubeDNSToHost requires hostDNS.enabled to be true (.machine.features.hostDNS)"))
|
||||
}
|
||||
|
||||
if !c.Machine().Features().HostDNS().ForwardKubeDNSToHost() {
|
||||
result = multierror.Append(result, errors.New("feature HostDNS should forward kube-dns to host in container mode (.machine.features.hostDNS.forwardKubeDNSToHost)"))
|
||||
}
|
||||
}
|
||||
|
||||
if c.Machine().Features().HostDNS().ForwardKubeDNSToHost() && !c.Machine().Features().HostDNS().Enabled() {
|
||||
result = multierror.Append(result, errors.New("feature hostDNS.forwardKubeDNSToHost requires hostDNS.enabled to be true (.machine.features.hostDNS)"))
|
||||
}
|
||||
|
||||
if t := c.Machine().Type(); t != machine.TypeUnknown && t.String() != c.MachineConfig.MachineType {
|
||||
|
||||
@ -2020,7 +2020,7 @@ func TestValidate(t *testing.T) {
|
||||
},
|
||||
MachineFeatures: &v1alpha1.FeaturesConfig{
|
||||
HostDNSSupport: &v1alpha1.HostDNSConfig{
|
||||
HostDNSEnabled: new(false),
|
||||
HostDNSConfigEnabled: new(false),
|
||||
HostDNSForwardKubeDNSToHost: new(true),
|
||||
},
|
||||
},
|
||||
|
||||
@ -1114,8 +1114,8 @@ func (in *FlannelCNIConfig) DeepCopy() *FlannelCNIConfig {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HostDNSConfig) DeepCopyInto(out *HostDNSConfig) {
|
||||
*out = *in
|
||||
if in.HostDNSEnabled != nil {
|
||||
in, out := &in.HostDNSEnabled, &out.HostDNSEnabled
|
||||
if in.HostDNSConfigEnabled != nil {
|
||||
in, out := &in.HostDNSConfigEnabled, &out.HostDNSConfigEnabled
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
|
||||
@ -35,11 +35,22 @@ searchDomains:
|
||||
disableDefault: true # Disable default search domain configuration from hostname FQDN.
|
||||
{{< /highlight >}}
|
||||
|
||||
{{< highlight yaml >}}
|
||||
apiVersion: v1alpha1
|
||||
kind: ResolverConfig
|
||||
# Configuration for host DNS resolver.
|
||||
hostDNS:
|
||||
enabled: true # Enable host DNS caching resolver.
|
||||
forwardKubeDNSToHost: true # Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.
|
||||
resolveMemberNames: true # Resolve member hostnames using the host DNS resolver.
|
||||
{{< /highlight >}}
|
||||
|
||||
|
||||
| Field | Type | Description | Value(s) |
|
||||
|-------|------|-------------|----------|
|
||||
|`nameservers` |<a href="#ResolverConfig.nameservers.">[]NameserverConfig</a> |A list of nameservers (DNS servers) to use for resolving domain names.<br><br>Nameservers are used to resolve domain names on the host, and they are also<br>propagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.<br><br>This overrides any nameservers obtained via DHCP or platform configuration.<br>Default configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers. | |
|
||||
|`searchDomains` |<a href="#ResolverConfig.searchDomains">SearchDomainsConfig</a> |Configuration for search domains (in /etc/resolv.conf).<br><br>The default is to derive search domains from the hostname FQDN. | |
|
||||
|`hostDNS` |<a href="#ResolverConfig.hostDNS">HostDNSConfig</a> |Configuration for host DNS resolver.<br><br>This configures a local DNS caching resolver on the host to improve DNS resolution performance and reliability. | |
|
||||
|
||||
|
||||
|
||||
@ -79,5 +90,23 @@ SearchDomainsConfig represents search domains configuration.
|
||||
|
||||
|
||||
|
||||
## hostDNS {#ResolverConfig.hostDNS}
|
||||
|
||||
HostDNSConfig represents host DNS configuration.
|
||||
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Value(s) |
|
||||
|-------|------|-------------|----------|
|
||||
|`enabled` |bool |Enable host DNS caching resolver.<br><br>When enabled, a local DNS caching resolver is deployed on the host to improve DNS resolution performance and reliability.<br>Upstream DNS servers for the host resolver are configured using the `nameservers` field in this config document. | |
|
||||
|`forwardKubeDNSToHost` |bool |Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.<br><br>When enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of<br>using configured upstream DNS resolvers directly). | |
|
||||
|`resolveMemberNames` |bool |Resolve member hostnames using the host DNS resolver.<br><br>When enabled, cluster member hostnames and node names are resolved using the host DNS resolver.<br>This requires service discovery to be enabled. | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -681,7 +681,6 @@ kubernetesTalosAPIAccess:
|
||||
{{< /highlight >}}</details> | |
|
||||
|`diskQuotaSupport` |bool |Enable XFS project quota support for EPHEMERAL partition and user disks.<br>Also enables kubelet tracking of ephemeral disk usage in the kubelet via quota. | |
|
||||
|`kubePrism` |<a href="#Config.machine.features.kubePrism">KubePrism</a> |KubePrism - local proxy/load balancer on defined port that will distribute<br>requests to all API servers in the cluster. | |
|
||||
|`hostDNS` |<a href="#Config.machine.features.hostDNS">HostDNSConfig</a> |Configures host DNS caching resolver. | |
|
||||
|`imageCache` |<a href="#Config.machine.features.imageCache">ImageCacheConfig</a> |Enable Image Cache feature. | |
|
||||
|`nodeAddressSortAlgorithm` |string |Select the node address sort algorithm.<br>The 'v1' algorithm sorts addresses by the address itself.<br>The 'v2' algorithm prefers more specific prefixes.<br>If unset, defaults to 'v1'. | |
|
||||
|
||||
@ -736,24 +735,6 @@ KubePrism describes the configuration for the KubePrism load balancer.
|
||||
|
||||
|
||||
|
||||
#### hostDNS {#Config.machine.features.hostDNS}
|
||||
|
||||
HostDNSConfig describes the configuration for the host DNS resolver.
|
||||
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Value(s) |
|
||||
|-------|------|-------------|----------|
|
||||
|`enabled` |bool |Enable host DNS caching resolver. | |
|
||||
|`forwardKubeDNSToHost` |bool |Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.<br><br>When enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of<br>using configured upstream DNS resolvers directly). | |
|
||||
|`resolveMemberNames` |bool |Resolve member hostnames using the host DNS resolver.<br><br>When enabled, cluster member hostnames and node names are resolved using the host DNS resolver.<br>This requires service discovery to be enabled. | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### imageCache {#Config.machine.features.imageCache}
|
||||
|
||||
ImageCacheConfig describes the configuration for the Image Cache feature.
|
||||
|
||||
@ -2226,6 +2226,34 @@
|
||||
],
|
||||
"description": "HCloudVIPConfig is a config document to configure virtual IP using Hetzner Cloud APIs for announcement.\\nVirtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server.\\nAny other use cases are not supported and may lead to unexpected behavior.\\nVirtual IP will be announced from only one node at a time using Hetzner Cloud APIs.\\n"
|
||||
},
|
||||
"network.HostDNSConfig": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"title": "enabled",
|
||||
"description": "Enable host DNS caching resolver.\n\nWhen enabled, a local DNS caching resolver is deployed on the host to improve DNS resolution performance and reliability.\nUpstream DNS servers for the host resolver are configured using the nameservers field in this config document.\n",
|
||||
"markdownDescription": "Enable host DNS caching resolver.\n\nWhen enabled, a local DNS caching resolver is deployed on the host to improve DNS resolution performance and reliability.\nUpstream DNS servers for the host resolver are configured using the `nameservers` field in this config document.",
|
||||
"x-intellij-html-description": "\u003cp\u003eEnable host DNS caching resolver.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, a local DNS caching resolver is deployed on the host to improve DNS resolution performance and reliability.\nUpstream DNS servers for the host resolver are configured using the \u003ccode\u003enameservers\u003c/code\u003e field in this config document.\u003c/p\u003e\n"
|
||||
},
|
||||
"forwardKubeDNSToHost": {
|
||||
"type": "boolean",
|
||||
"title": "forwardKubeDNSToHost",
|
||||
"description": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\n",
|
||||
"markdownDescription": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).",
|
||||
"x-intellij-html-description": "\u003cp\u003eUse the host DNS resolver as upstream for Kubernetes CoreDNS pods.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\u003c/p\u003e\n"
|
||||
},
|
||||
"resolveMemberNames": {
|
||||
"type": "boolean",
|
||||
"title": "resolveMemberNames",
|
||||
"description": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\n",
|
||||
"markdownDescription": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.",
|
||||
"x-intellij-html-description": "\u003cp\u003eResolve member hostnames using the host DNS resolver.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "HostDNSConfig represents host DNS configuration."
|
||||
},
|
||||
"network.HostnameConfigV1Alpha1": {
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
@ -2705,6 +2733,13 @@
|
||||
"description": "Configuration for search domains (in /etc/resolv.conf).\n\nThe default is to derive search domains from the hostname FQDN.\n",
|
||||
"markdownDescription": "Configuration for search domains (in /etc/resolv.conf).\n\nThe default is to derive search domains from the hostname FQDN.",
|
||||
"x-intellij-html-description": "\u003cp\u003eConfiguration for search domains (in /etc/resolv.conf).\u003c/p\u003e\n\n\u003cp\u003eThe default is to derive search domains from the hostname FQDN.\u003c/p\u003e\n"
|
||||
},
|
||||
"hostDNS": {
|
||||
"$ref": "#/$defs/network.HostDNSConfig",
|
||||
"title": "hostDNS",
|
||||
"description": "Configuration for host DNS resolver.\n\nThis configures a local DNS caching resolver on the host to improve DNS resolution performance and reliability.\n",
|
||||
"markdownDescription": "Configuration for host DNS resolver.\n\nThis configures a local DNS caching resolver on the host to improve DNS resolution performance and reliability.",
|
||||
"x-intellij-html-description": "\u003cp\u003eConfiguration for host DNS resolver.\u003c/p\u003e\n\n\u003cp\u003eThis configures a local DNS caching resolver on the host to improve DNS resolution performance and reliability.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@ -4674,13 +4709,6 @@
|
||||
"markdownDescription": "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.",
|
||||
"x-intellij-html-description": "\u003cp\u003eKubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\u003c/p\u003e\n"
|
||||
},
|
||||
"hostDNS": {
|
||||
"$ref": "#/$defs/v1alpha1.HostDNSConfig",
|
||||
"title": "hostDNS",
|
||||
"description": "Configures host DNS caching resolver.\n",
|
||||
"markdownDescription": "Configures host DNS caching resolver.",
|
||||
"x-intellij-html-description": "\u003cp\u003eConfigures host DNS caching resolver.\u003c/p\u003e\n"
|
||||
},
|
||||
"imageCache": {
|
||||
"$ref": "#/$defs/v1alpha1.ImageCacheConfig",
|
||||
"title": "imageCache",
|
||||
@ -4724,34 +4752,6 @@
|
||||
"type": "object",
|
||||
"description": "FlannelCNIConfig represents the Flannel CNI configuration options."
|
||||
},
|
||||
"v1alpha1.HostDNSConfig": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"title": "enabled",
|
||||
"description": "Enable host DNS caching resolver.\n",
|
||||
"markdownDescription": "Enable host DNS caching resolver.",
|
||||
"x-intellij-html-description": "\u003cp\u003eEnable host DNS caching resolver.\u003c/p\u003e\n"
|
||||
},
|
||||
"forwardKubeDNSToHost": {
|
||||
"type": "boolean",
|
||||
"title": "forwardKubeDNSToHost",
|
||||
"description": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\n",
|
||||
"markdownDescription": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).",
|
||||
"x-intellij-html-description": "\u003cp\u003eUse the host DNS resolver as upstream for Kubernetes CoreDNS pods.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\u003c/p\u003e\n"
|
||||
},
|
||||
"resolveMemberNames": {
|
||||
"type": "boolean",
|
||||
"title": "resolveMemberNames",
|
||||
"description": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\n",
|
||||
"markdownDescription": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.",
|
||||
"x-intellij-html-description": "\u003cp\u003eResolve member hostnames using the host DNS resolver.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "HostDNSConfig describes the configuration for the host DNS resolver."
|
||||
},
|
||||
"v1alpha1.ImageCacheConfig": {
|
||||
"properties": {
|
||||
"localEnabled": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user