feat: implement time and resolvers multi-doc configuration

Fixes #10954

Fixes #10955

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
Andrey Smirnov 2025-11-11 21:26:43 +04:00
parent 399240be3a
commit 5e1de00359
No known key found for this signature in database
GPG Key ID: 322C6F63F594CE7C
36 changed files with 2045 additions and 645 deletions

View File

@ -187,6 +187,13 @@ When `volumeType = "disk"`:
When using UEFI boot with systemd-boot as bootloader (on new installs of Talos from 1.10+ onwards), Talos will now not touch the UEFI boot order.
Talos 1.11 made a fix to create UEFI boot entry and set the boot order as first entry, but this behavior caused issues on some systems.
To avoid further issues, Talos will now only create the UEFI boot entry if it does not exist, but will not modify the boot order.
"""
[notes.network-configuration]
title = "Network Configuration"
description = """\
The network configuration under `.machine.network` (with the exception of KubeSpan) has been deprecated, but it is still supported for backwards compatibility.
New configuration documents were created to replace it, they will be documented in the future.
"""
[make_deps]

View File

@ -39,7 +39,11 @@ func (r *TimeServer) Register(s *grpc.Server) {
// Time issues a query to the configured ntp server and displays the results.
func (r *TimeServer) Time(ctx context.Context, in *emptypb.Empty) (reply *timeapi.TimeResponse, err error) {
timeServers := r.ConfigProvider.Config().Machine().Time().Servers()
var timeServers []string
if cfg := r.ConfigProvider.Config().NetworkTimeSyncConfig(); cfg != nil {
timeServers = cfg.Servers()
}
if len(timeServers) == 0 {
timeServers = []string{constants.DefaultNTPServer}

View File

@ -82,7 +82,7 @@ func (ctrl *ResolverConfigController) Run(ctx context.Context, r controller.Runt
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting config: %w", err)
}
} else if cfg.Config().Machine() != nil {
} else {
cfgProvider = cfg.Config()
}
@ -111,7 +111,7 @@ func (ctrl *ResolverConfigController) Run(ctx context.Context, r controller.Runt
// parse machine configuration for specs
if cfgProvider != nil {
if configServers, ok := ctrl.parseMachineConfiguration(logger, cfgProvider); ok {
if configServers, ok := ctrl.parseMachineConfiguration(cfgProvider); ok {
specs = append(specs, configServers)
}
}
@ -181,8 +181,8 @@ func (ctrl *ResolverConfigController) getDefault(cfg talosconfig.Config, hostnam
spec.ConfigLayer = network.ConfigDefault
if cfg == nil ||
cfg.Machine() == nil ||
cfg.Machine().Network().DisableSearchDomain() ||
cfg.NetworkResolverConfig() == nil ||
cfg.NetworkResolverConfig().DisableSearchDomain() ||
hostnameStatus == nil ||
hostnameStatus.Domainname == "" {
return spec
@ -215,27 +215,21 @@ func (ctrl *ResolverConfigController) parseCmdline(logger *zap.Logger) (spec net
return spec
}
func (ctrl *ResolverConfigController) parseMachineConfiguration(logger *zap.Logger, cfgProvider talosconfig.Config) (network.ResolverSpecSpec, bool) {
func (ctrl *ResolverConfigController) parseMachineConfiguration(cfgProvider talosconfig.Config) (network.ResolverSpecSpec, bool) {
var spec network.ResolverSpecSpec
resolvers := cfgProvider.Machine().Network().Resolvers()
searchDomains := cfgProvider.Machine().Network().SearchDomains()
if cfgProvider.NetworkResolverConfig() == nil {
return spec, false
}
resolvers := cfgProvider.NetworkResolverConfig().Resolvers()
searchDomains := cfgProvider.NetworkResolverConfig().SearchDomains()
if len(resolvers) == 0 && len(searchDomains) == 0 {
return spec, false
}
for _, resolver := range resolvers {
server, err := netip.ParseAddr(resolver)
if err != nil {
logger.Warn("failed to parse DNS server", zap.String("server", resolver), zap.Error(err))
continue
}
spec.DNSServers = append(spec.DNSServers, server)
}
spec.DNSServers = slices.Clone(resolvers)
spec.SearchDomains = slices.Clone(searchDomains)
spec.ConfigLayer = network.ConfigMachineConfiguration

View File

@ -5,29 +5,21 @@
package network_test
import (
"context"
"net/netip"
"net/url"
"sync"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/controller/runtime"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/resource/rtestutils"
"github.com/cosi-project/runtime/pkg/state"
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
"github.com/siderolabs/go-pointer"
"github.com/siderolabs/go-procfs/procfs"
"github.com/siderolabs/go-retry/retry"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"go.uber.org/zap/zaptest"
"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/resources/config"
@ -35,70 +27,14 @@ import (
)
type ResolverConfigSuite struct {
suite.Suite
state state.State
runtime *runtime.Runtime
wg sync.WaitGroup
ctx context.Context //nolint:containedctx
ctxCancel context.CancelFunc
}
func (suite *ResolverConfigSuite) State() state.State { return suite.state }
func (suite *ResolverConfigSuite) Ctx() context.Context { return suite.ctx }
func (suite *ResolverConfigSuite) SetupTest() {
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute)
suite.state = state.WrapCore(namespaced.NewState(inmem.Build))
var err error
suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T()))
suite.Require().NoError(err)
}
func (suite *ResolverConfigSuite) startRuntime() {
suite.wg.Add(1)
go func() {
defer suite.wg.Done()
suite.Assert().NoError(suite.runtime.Run(suite.ctx))
}()
}
func (suite *ResolverConfigSuite) assertResolvers(requiredIDs []string, check func(*network.ResolverSpec, *assert.Assertions)) {
assertResources(suite.ctx, suite.T(), suite.state, requiredIDs, check, rtestutils.WithNamespace(network.ConfigNamespaceName))
}
func (suite *ResolverConfigSuite) assertNoResolver(id string) error {
resources, err := suite.state.List(
suite.ctx,
resource.NewMetadata(network.ConfigNamespaceName, network.ResolverSpecType, "", resource.VersionUndefined),
)
if err != nil {
return err
}
for _, res := range resources.Items {
if res.Metadata().ID() == id {
return retry.ExpectedErrorf("spec %q is still there", id)
}
}
return nil
ctest.DefaultSuite
}
func (suite *ResolverConfigSuite) TestDefaults() {
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.ResolverConfigController{}))
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.ResolverConfigController{}))
suite.startRuntime()
suite.assertResolvers(
ctest.AssertResources(
suite,
[]string{
"default/resolvers",
}, func(r *network.ResolverSpec, asrt *assert.Assertions) {
@ -111,16 +47,17 @@ func (suite *ResolverConfigSuite) TestDefaults() {
asrt.Empty(r.TypedSpec().SearchDomains)
asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
}
func (suite *ResolverConfigSuite) TestWithHostnameStatus() {
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.ResolverConfigController{}))
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.ResolverConfigController{}))
hostnameStatus := network.NewHostnameStatus(network.NamespaceName, network.HostnameID)
hostnameStatus.TypedSpec().Hostname = "irrelevant"
hostnameStatus.TypedSpec().Domainname = "example.org"
suite.Require().NoError(suite.state.Create(suite.ctx, hostnameStatus))
suite.Create(hostnameStatus)
u, err := url.Parse("https://foo:6443")
suite.Require().NoError(err)
@ -143,11 +80,10 @@ func (suite *ResolverConfigSuite) TestWithHostnameStatus() {
),
)
suite.Require().NoError(suite.state.Create(suite.ctx, cfg))
suite.Create(cfg)
suite.startRuntime()
suite.assertResolvers(
ctest.AssertResources(
suite,
[]string{
"default/resolvers",
}, func(r *network.ResolverSpec, asrt *assert.Assertions) {
@ -160,48 +96,52 @@ func (suite *ResolverConfigSuite) TestWithHostnameStatus() {
asrt.Equal([]string{"example.org"}, r.TypedSpec().SearchDomains)
asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
// make domain name empty
hostnameStatus.TypedSpec().Domainname = ""
suite.Require().NoError(suite.state.Update(suite.ctx, hostnameStatus))
suite.Update(hostnameStatus)
suite.assertResolvers(
ctest.AssertResources(
suite,
[]string{
"default/resolvers",
}, func(r *network.ResolverSpec, asrt *assert.Assertions) {
asrt.Empty(r.TypedSpec().SearchDomains)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
// bring back domain name, but disable via machine config
hostnameStatus.TypedSpec().Domainname = "example.org"
suite.Require().NoError(suite.state.Update(suite.ctx, hostnameStatus))
suite.Update(hostnameStatus)
cfg.Container().RawV1Alpha1().MachineConfig.MachineNetwork.NetworkDisableSearchDomain = pointer.To(true)
suite.Require().NoError(suite.state.Update(suite.ctx, cfg))
cfg.Container().RawV1Alpha1().MachineConfig.MachineNetwork.NetworkDisableSearchDomain = pointer.To(true) //nolint:staticcheck
suite.Update(cfg)
suite.assertResolvers(
ctest.AssertResources(
suite,
[]string{
"default/resolvers",
}, func(r *network.ResolverSpec, asrt *assert.Assertions) {
asrt.Empty(r.TypedSpec().SearchDomains)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
}
func (suite *ResolverConfigSuite) TestCmdline() {
suite.Require().NoError(
suite.runtime.RegisterController(
suite.Runtime().RegisterController(
&netctrl.ResolverConfigController{
Cmdline: procfs.NewCmdline("ip=172.20.0.2:172.21.0.1:172.20.0.1:255.255.255.0:master1:eth1::10.0.0.1:10.0.0.2:10.0.0.1"),
},
),
)
suite.startRuntime()
suite.assertResolvers(
ctest.AssertResources(
suite,
[]string{
"cmdline/resolvers",
}, func(r *network.ResolverSpec, asrt *assert.Assertions) {
@ -213,13 +153,12 @@ func (suite *ResolverConfigSuite) TestCmdline() {
)
asrt.Empty(r.TypedSpec().SearchDomains)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
}
func (suite *ResolverConfigSuite) TestMachineConfiguration() {
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.ResolverConfigController{}))
suite.startRuntime()
func (suite *ResolverConfigSuite) TestMachineConfigurationLegacy() {
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.ResolverConfigController{}))
u, err := url.Parse("https://foo:6443")
suite.Require().NoError(err)
@ -245,9 +184,10 @@ func (suite *ResolverConfigSuite) TestMachineConfiguration() {
),
)
suite.Require().NoError(suite.state.Create(suite.ctx, cfg))
suite.Create(cfg)
suite.assertResolvers(
ctest.AssertResources(
suite,
[]string{
"configuration/resolvers",
}, func(r *network.ResolverSpec, asrt *assert.Assertions) {
@ -263,32 +203,72 @@ func (suite *ResolverConfigSuite) TestMachineConfiguration() {
r.TypedSpec().SearchDomains,
)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
ctest.UpdateWithConflicts(suite, cfg, func(r *config.MachineConfig) error {
r.Container().RawV1Alpha1().MachineConfig.MachineNetwork.NameServers = nil
r.Container().RawV1Alpha1().MachineConfig.MachineNetwork.Searches = nil
r.Container().RawV1Alpha1().MachineConfig.MachineNetwork.NameServers = nil //nolint:staticcheck
r.Container().RawV1Alpha1().MachineConfig.MachineNetwork.Searches = nil //nolint:staticcheck
return nil
})
suite.Assert().NoError(
retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertNoResolver("configuration/resolvers")
},
),
)
ctest.AssertNoResource[*network.ResolverSpec](suite, "configuration/resolvers", rtestutils.WithNamespace(network.ConfigNamespaceName))
}
func (suite *ResolverConfigSuite) TearDownTest() {
suite.T().Log("tear down")
func (suite *ResolverConfigSuite) TestMachineConfigurationNewStyle() {
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.ResolverConfigController{}))
suite.ctxCancel()
rc := networkcfg.NewResolverConfigV1Alpha1()
rc.ResolverNameservers = []networkcfg.NameserverConfig{
{
Address: networkcfg.Addr{Addr: netip.MustParseAddr("2.2.2.2")},
},
{
Address: networkcfg.Addr{Addr: netip.MustParseAddr("3.3.3.3")},
},
}
rc.ResolverSearchDomains = networkcfg.SearchDomainsConfig{
SearchDomains: []string{"example.com", "example.org"},
}
suite.wg.Wait()
ctr, err := container.New(rc)
suite.Require().NoError(err)
cfg := config.NewMachineConfig(ctr)
suite.Create(cfg)
ctest.AssertResources(
suite,
[]string{
"configuration/resolvers",
}, func(r *network.ResolverSpec, asrt *assert.Assertions) {
asrt.Equal(
[]netip.Addr{
netip.MustParseAddr("2.2.2.2"),
netip.MustParseAddr("3.3.3.3"),
}, r.TypedSpec().DNSServers,
)
asrt.Equal(
[]string{"example.com", "example.org"},
r.TypedSpec().SearchDomains,
)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
suite.Destroy(cfg)
ctest.AssertNoResource[*network.ResolverSpec](suite, "configuration/resolvers", rtestutils.WithNamespace(network.ConfigNamespaceName))
}
func TestResolverConfigSuite(t *testing.T) {
suite.Run(t, new(ResolverConfigSuite))
t.Parallel()
suite.Run(t, &ResolverConfigSuite{
DefaultSuite: ctest.DefaultSuite{
Timeout: 10 * time.Second,
},
})
}

View File

@ -75,7 +75,7 @@ func (ctrl *TimeServerConfigController) Run(ctx context.Context, r controller.Ru
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting config: %w", err)
}
} else if cfg.Config().Machine() != nil {
} else {
cfgProvider = cfg.Config()
}
@ -193,11 +193,11 @@ func (ctrl *TimeServerConfigController) parseCmdline(logger *zap.Logger) (spec n
}
func (ctrl *TimeServerConfigController) parseMachineConfiguration(cfgProvider talosconfig.Config) (spec network.TimeServerSpecSpec) {
if len(cfgProvider.Machine().Time().Servers()) == 0 {
if cfgProvider.NetworkTimeSyncConfig() == nil {
return spec
}
spec.NTPServers = slices.Clone(cfgProvider.Machine().Time().Servers())
spec.NTPServers = slices.Clone(cfgProvider.NetworkTimeSyncConfig().Servers())
spec.ConfigLayer = network.ConfigMachineConfiguration
return spec

View File

@ -5,27 +5,19 @@
package network_test
import (
"context"
"net/url"
"sync"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/controller/runtime"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/resource/rtestutils"
"github.com/cosi-project/runtime/pkg/state"
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
"github.com/siderolabs/go-procfs/procfs"
"github.com/siderolabs/go-retry/retry"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"go.uber.org/zap/zaptest"
"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/resources/config"
@ -33,106 +25,46 @@ import (
)
type TimeServerConfigSuite struct {
suite.Suite
state state.State
runtime *runtime.Runtime
wg sync.WaitGroup
ctx context.Context //nolint:containedctx
ctxCancel context.CancelFunc
}
func (suite *TimeServerConfigSuite) State() state.State { return suite.state }
func (suite *TimeServerConfigSuite) Ctx() context.Context { return suite.ctx }
func (suite *TimeServerConfigSuite) SetupTest() {
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute)
suite.state = state.WrapCore(namespaced.NewState(inmem.Build))
var err error
suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T()))
suite.Require().NoError(err)
}
func (suite *TimeServerConfigSuite) startRuntime() {
suite.wg.Add(1)
go func() {
defer suite.wg.Done()
suite.Assert().NoError(suite.runtime.Run(suite.ctx))
}()
}
func (suite *TimeServerConfigSuite) assertTimeServers(
requiredIDs []string,
check func(*network.TimeServerSpec, *assert.Assertions),
) {
assertResources(suite.ctx, suite.T(), suite.state, requiredIDs, check, rtestutils.WithNamespace(network.ConfigNamespaceName))
}
func (suite *TimeServerConfigSuite) assertNoTimeServer(id string) error {
resources, err := suite.state.List(
suite.ctx,
resource.NewMetadata(network.ConfigNamespaceName, network.TimeServerSpecType, "", resource.VersionUndefined),
)
if err != nil {
return err
}
for _, res := range resources.Items {
if res.Metadata().ID() == id {
return retry.ExpectedErrorf("spec %q is still there", id)
}
}
return nil
ctest.DefaultSuite
}
func (suite *TimeServerConfigSuite) TestDefaults() {
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.TimeServerConfigController{}))
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.TimeServerConfigController{}))
suite.startRuntime()
suite.assertTimeServers(
ctest.AssertResources(
suite,
[]string{
"default/timeservers",
}, func(r *network.TimeServerSpec, asrt *assert.Assertions) {
asrt.Equal([]string{constants.DefaultNTPServer}, r.TypedSpec().NTPServers)
asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
}
func (suite *TimeServerConfigSuite) TestCmdline() {
suite.Require().NoError(
suite.runtime.RegisterController(
suite.Runtime().RegisterController(
&netctrl.TimeServerConfigController{
Cmdline: procfs.NewCmdline("ip=172.20.0.2:172.21.0.1:172.20.0.1:255.255.255.0:master1:eth1::10.0.0.1:10.0.0.2:10.0.0.1"),
},
),
)
suite.startRuntime()
suite.assertTimeServers(
ctest.AssertResources(
suite,
[]string{
"cmdline/timeservers",
}, func(r *network.TimeServerSpec, asrt *assert.Assertions) {
asrt.Equal([]string{"10.0.0.1"}, r.TypedSpec().NTPServers)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
}
func (suite *TimeServerConfigSuite) TestMachineConfiguration() {
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.TimeServerConfigController{}))
suite.startRuntime()
func (suite *TimeServerConfigSuite) TestMachineConfigurationLegacy() {
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.TimeServerConfigController{}))
u, err := url.Parse("https://foo:6443")
suite.Require().NoError(err)
@ -157,39 +89,62 @@ func (suite *TimeServerConfigSuite) TestMachineConfiguration() {
),
)
suite.Require().NoError(suite.state.Create(suite.ctx, cfg))
suite.Create(cfg)
suite.assertTimeServers(
ctest.AssertResources(
suite,
[]string{
"configuration/timeservers",
}, func(r *network.TimeServerSpec, asrt *assert.Assertions) {
asrt.Equal([]string{"za.pool.ntp.org", "pool.ntp.org"}, r.TypedSpec().NTPServers)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
ctest.UpdateWithConflicts(suite, cfg, func(r *config.MachineConfig) error {
r.Container().RawV1Alpha1().MachineConfig.MachineTime = nil
r.Container().RawV1Alpha1().MachineConfig.MachineTime = nil //nolint:staticcheck
return nil
})
suite.Assert().NoError(
retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertNoTimeServer("configuration/timeservers")
},
),
)
ctest.AssertNoResource[*network.TimeServerSpec](suite, "configuration/timeservers", rtestutils.WithNamespace(network.ConfigNamespaceName))
}
func (suite *TimeServerConfigSuite) TearDownTest() {
suite.T().Log("tear down")
func (suite *TimeServerConfigSuite) TestMachineConfigurationNewStyle() {
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.TimeServerConfigController{}))
suite.ctxCancel()
tsc := networkcfg.NewTimeSyncConfigV1Alpha1()
tsc.TimeNTP = &networkcfg.NTPConfig{
Servers: []string{"za.pool.ntp.org", "pool.ntp.org"},
}
suite.wg.Wait()
ctr, err := container.New(tsc)
suite.Require().NoError(err)
cfg := config.NewMachineConfig(ctr)
suite.Create(cfg)
ctest.AssertResources(
suite,
[]string{
"configuration/timeservers",
}, func(r *network.TimeServerSpec, asrt *assert.Assertions) {
asrt.Equal([]string{"za.pool.ntp.org", "pool.ntp.org"}, r.TypedSpec().NTPServers)
},
rtestutils.WithNamespace(network.ConfigNamespaceName),
)
suite.Destroy(cfg)
ctest.AssertNoResource[*network.TimeServerSpec](suite, "configuration/timeservers", rtestutils.WithNamespace(network.ConfigNamespaceName))
}
func TestTimeServerConfigSuite(t *testing.T) {
suite.Run(t, new(TimeServerConfigSuite))
t.Parallel()
suite.Run(t, &TimeServerConfigSuite{
DefaultSuite: ctest.DefaultSuite{
Timeout: 10 * time.Second,
},
})
}

View File

@ -6,83 +6,20 @@
package network_test
import (
"context"
"slices"
"sync"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/controller/runtime"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/state"
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
"github.com/siderolabs/go-retry/retry"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"go.uber.org/zap/zaptest"
"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/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
)
type TimeServerSpecSuite struct {
suite.Suite
state state.State
runtime *runtime.Runtime
wg sync.WaitGroup
ctx context.Context //nolint:containedctx
ctxCancel context.CancelFunc
}
func (suite *TimeServerSpecSuite) SetupTest() {
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute)
suite.state = state.WrapCore(namespaced.NewState(inmem.Build))
var err error
suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T()))
suite.Require().NoError(err)
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.TimeServerSpecController{}))
suite.startRuntime()
}
func (suite *TimeServerSpecSuite) startRuntime() {
suite.wg.Add(1)
go func() {
defer suite.wg.Done()
suite.Assert().NoError(suite.runtime.Run(suite.ctx))
}()
}
func (suite *TimeServerSpecSuite) assertStatus(id string, servers ...string) error {
r, err := suite.state.Get(
suite.ctx,
resource.NewMetadata(network.NamespaceName, network.TimeServerStatusType, id, resource.VersionUndefined),
)
if err != nil {
if state.IsNotFoundError(err) {
return retry.ExpectedError(err)
}
return err
}
status := r.(*network.TimeServerStatus) //nolint:forcetypeassert
if !slices.Equal(status.TypedSpec().NTPServers, servers) {
return retry.ExpectedErrorf("server list mismatch: %q != %q", status.TypedSpec().NTPServers, servers)
}
return nil
ctest.DefaultSuite
}
func (suite *TimeServerSpecSuite) TestSpec() {
@ -92,27 +29,26 @@ func (suite *TimeServerSpecSuite) TestSpec() {
ConfigLayer: network.ConfigDefault,
}
for _, res := range []resource.Resource{spec} {
suite.Require().NoError(suite.state.Create(suite.ctx, res), "%v", res.Spec())
}
suite.Create(spec)
suite.Assert().NoError(
retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertStatus("timeservers", constants.DefaultNTPServer)
},
),
ctest.AssertResource(
suite,
"timeservers",
func(status *network.TimeServerStatus, asrt *assert.Assertions) {
asrt.Equal([]string{constants.DefaultNTPServer}, status.TypedSpec().NTPServers)
},
)
}
func (suite *TimeServerSpecSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
}
func TestTimeServerSpecSuite(t *testing.T) {
suite.Run(t, new(TimeServerSpecSuite))
t.Parallel()
suite.Run(t, &TimeServerSpecSuite{
DefaultSuite: ctest.DefaultSuite{
Timeout: 5 * time.Second,
AfterSetup: func(suite *ctest.DefaultSuite) {
suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.TimeServerSpecController{}))
},
},
})
}

View File

@ -171,12 +171,12 @@ func (ctrl *SyncController) Run(ctx context.Context, r controller.Runtime, logge
syncDisabled = true
}
if cfg != nil && cfg.Config().Machine() != nil {
if cfg.Config().Machine().Time().Disabled() {
if cfg != nil && cfg.Config().NetworkTimeSyncConfig() != nil {
if cfg.Config().NetworkTimeSyncConfig().Disabled() {
syncDisabled = true
}
syncTimeout = cfg.Config().Machine().Time().BootTimeout()
syncTimeout = cfg.Config().NetworkTimeSyncConfig().BootTimeout()
}
if !timeSynced {

View File

@ -367,7 +367,7 @@ func (suite *SyncSuite) TestReconcileSyncChangeConfig() {
)
ctest.UpdateWithConflicts(suite, cfg, func(r *config.MachineConfig) error {
r.Container().RawV1Alpha1().MachineConfig.MachineTime = &v1alpha1.TimeConfig{
r.Container().RawV1Alpha1().MachineConfig.MachineTime = &v1alpha1.TimeConfig{ //nolint:staticcheck
TimeDisabled: pointer.To(true),
}

View File

@ -167,7 +167,7 @@ func (r *Runtime) CanApplyImmediate(cfg config.Provider) error {
if newConfig.MachineConfig != nil && currentConfig.MachineConfig != nil {
newConfig.MachineConfig.MachineCA = currentConfig.MachineConfig.MachineCA
newConfig.MachineConfig.MachineAcceptedCAs = currentConfig.MachineConfig.MachineAcceptedCAs
newConfig.MachineConfig.MachineTime = currentConfig.MachineConfig.MachineTime
newConfig.MachineConfig.MachineTime = currentConfig.MachineConfig.MachineTime //nolint:staticcheck
newConfig.MachineConfig.MachineCertSANs = currentConfig.MachineConfig.MachineCertSANs
newConfig.MachineConfig.MachineInstall = currentConfig.MachineConfig.MachineInstall
newConfig.MachineConfig.MachineNetwork = currentConfig.MachineConfig.MachineNetwork

View File

@ -21,6 +21,8 @@ type Config interface { //nolint:interfacebloat
RunDefaultDHCPOperators() bool
NetworkStaticHostConfig() []NetworkStaticHostConfig
NetworkHostnameConfig() NetworkHostnameConfig
NetworkResolverConfig() NetworkResolverConfig
NetworkTimeSyncConfig() NetworkTimeSyncConfig
NetworkCommonLinkConfigs() []NetworkCommonLinkConfig
NetworkLinkAliasConfigs() []NetworkLinkAliasConfig
NetworkDHCPConfigs() []NetworkDHCPConfig

View File

@ -27,7 +27,6 @@ type MachineConfig interface {
Security() Security
Network() MachineNetwork
Disks() []Disk
Time() Time
Env() Env
Files() ([]File, error)
Type() machine.Type
@ -143,11 +142,8 @@ type MachineScheduler interface {
//
// This is a legacy interface which is going to be decomposed and removed in the future.
type MachineNetwork interface {
Resolvers() []string
SearchDomains() []string
Devices() []Device
KubeSpan() KubeSpan
DisableSearchDomain() bool
}
// Device represents a network interface.
@ -321,14 +317,6 @@ type NetworkDeviceSelector interface {
Physical() *bool
}
// Time defines the requirements for a config that pertains to time related
// options.
type Time interface {
Disabled() bool
Servers() []string
BootTimeout() time.Duration
}
// Kubelet defines the requirements for a config that pertains to kubelet
// related options.
//

View File

@ -112,6 +112,21 @@ type NetworkHostnameConfig interface {
AutoHostname() nethelpers.AutoHostnameKind
}
// NetworkResolverConfig defines a resolver configuration.
type NetworkResolverConfig interface {
Resolvers() []netip.Addr
SearchDomains() []string
DisableSearchDomain() bool
}
// NetworkTimeSyncConfig defines the requirements for a config that pertains to time related
// options.
type NetworkTimeSyncConfig interface {
Disabled() bool
Servers() []string
BootTimeout() time.Duration
}
// NetworkPhysicalLinkConfig defines a physical network link configuration.
type NetworkPhysicalLinkConfig interface {
PhysicalLinkConfig()

View File

@ -64,7 +64,7 @@ func TestSchemaValidation(t *testing.T) {
{
name: "v1alpha1_invalid-duration",
config: newV1Alpha1Config(t, nil, func(rawConfig map[string]any) {
setNestedField(t, rawConfig, "100y", "machine", "time", "bootTimeout")
setNestedField(t, rawConfig, "100y", "cluster", "adminKubeconfig", "certLifetime")
}),
expectedErrorContains: `does not match pattern`,
},

View File

@ -302,6 +302,38 @@ func (container *Container) NetworkHostnameConfig() config.NetworkHostnameConfig
return nil
}
// NetworkResolverConfig implements config.Config interface.
func (container *Container) NetworkResolverConfig() config.NetworkResolverConfig {
// first check if we have a dedicated document
matching := findMatchingDocs[config.NetworkResolverConfig](container.documents)
if len(matching) > 0 {
return matching[0]
}
// fallback to v1alpha1
if container.v1alpha1Config != nil {
return container.v1alpha1Config
}
return nil
}
// NetworkTimeSyncConfig implements config.Config interface.
func (container *Container) NetworkTimeSyncConfig() config.NetworkTimeSyncConfig {
// first check if we have a dedicated document
matching := findMatchingDocs[config.NetworkTimeSyncConfig](container.documents)
if len(matching) > 0 {
return matching[0]
}
// fallback to v1alpha1
if container.v1alpha1Config != nil {
return container.v1alpha1Config.NetworkTimeSyncConfig()
}
return nil
}
// NetworkCommonLinkConfigs implements config.Config interface.
func (container *Container) NetworkCommonLinkConfigs() []config.NetworkCommonLinkConfig {
return findMatchingDocs[config.NetworkCommonLinkConfig](container.documents)

View File

@ -2249,6 +2249,101 @@
"type": "object",
"description": "LinkSelector selects a link to alias."
},
"network.NTPConfig": {
"properties": {
"servers": {
"items": {
"type": "string"
},
"type": "array",
"title": "servers",
"description": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to time.cloudflare.com.\n",
"markdownDescription": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to `time.cloudflare.com`.",
"x-intellij-html-description": "\u003cp\u003eSpecifies time (NTP) servers to use for setting the system time.\nDefaults to \u003ccode\u003etime.cloudflare.com\u003c/code\u003e.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"description": "NTPConfig represents a NTP server configuration."
},
"network.NameserverConfig": {
"properties": {
"address": {
"type": "string",
"pattern": "^[0-9a-f.:]+$",
"title": "address",
"description": "The IP address of the nameserver.\n",
"markdownDescription": "The IP address of the nameserver.",
"x-intellij-html-description": "\u003cp\u003eThe IP address of the nameserver.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"description": "NameserverConfig represents a single nameserver configuration."
},
"network.PTPConfig": {
"properties": {
"devices": {
"items": {
"type": "string"
},
"type": "array",
"title": "devices",
"description": "description: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\n\nA PTP device is typically represented as a character device file in the /dev directory,\n\n\nsuch as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.\n",
"markdownDescription": "description: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\n\n A PTP device is typically represented as a character device file in the /dev directory,\n such as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.",
"x-intellij-html-description": "\u003cp\u003edescription: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\u003c/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eA PTP device is typically represented as a character device file in the /dev directory,\n\u003c/code\u003e\u003c/pre\u003e\n\n\u003cp\u003esuch as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"description": "PTPConfig represents a PTP (Precision Time Protocol) configuration."
},
"network.ResolverConfigV1Alpha1": {
"properties": {
"apiVersion": {
"enum": [
"v1alpha1"
],
"title": "apiVersion",
"description": "apiVersion is the API version of the resource.\n",
"markdownDescription": "apiVersion is the API version of the resource.",
"x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n"
},
"kind": {
"enum": [
"ResolverConfig"
],
"title": "kind",
"description": "kind is the kind of the resource.\n",
"markdownDescription": "kind is the kind of the resource.",
"x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n"
},
"nameservers": {
"items": {
"$ref": "#/$defs/network.NameserverConfig"
},
"type": "array",
"title": "nameservers",
"description": "A list of nameservers (DNS servers) to use for resolving domain names.\n\nNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\n\nThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.\n",
"markdownDescription": "A list of nameservers (DNS servers) to use for resolving domain names.\n\nNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\n\nThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.",
"x-intellij-html-description": "\u003cp\u003eA list of nameservers (DNS servers) to use for resolving domain names.\u003c/p\u003e\n\n\u003cp\u003eNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\u003c/p\u003e\n\n\u003cp\u003eThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.\u003c/p\u003e\n"
},
"searchDomains": {
"$ref": "#/$defs/network.SearchDomainsConfig",
"title": "searchDomains",
"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"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"apiVersion",
"kind"
],
"description": "ResolverConfig is a config document to configure DNS resolving."
},
"network.RouteConfig": {
"properties": {
"destination": {
@ -2391,6 +2486,30 @@
"type": "object",
"description": "RulePortSelector is a port selector for the network rule."
},
"network.SearchDomainsConfig": {
"properties": {
"domains": {
"items": {
"type": "string"
},
"type": "array",
"title": "domains",
"description": "A list of search domains to be used for DNS resolution.\n\nSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if “example.com” is a search domain and a user tries to resolve\n“host”, the system will attempt to resolve “host.example.com”.\n\nThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.\n",
"markdownDescription": "A list of search domains to be used for DNS resolution.\n\nSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if \"example.com\" is a search domain and a user tries to resolve\n\"host\", the system will attempt to resolve \"host.example.com\".\n\nThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.",
"x-intellij-html-description": "\u003cp\u003eA list of search domains to be used for DNS resolution.\u003c/p\u003e\n\n\u003cp\u003eSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if \u0026ldquo;example.com\u0026rdquo; is a search domain and a user tries to resolve\n\u0026ldquo;host\u0026rdquo;, the system will attempt to resolve \u0026ldquo;host.example.com\u0026rdquo;.\u003c/p\u003e\n\n\u003cp\u003eThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.\u003c/p\u003e\n"
},
"disableDefault": {
"type": "boolean",
"title": "disableDefault",
"description": "Disable default search domain configuration from hostname FQDN.\n\nWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.\n",
"markdownDescription": "Disable default search domain configuration from hostname FQDN.\n\nWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.",
"x-intellij-html-description": "\u003cp\u003eDisable default search domain configuration from hostname FQDN.\u003c/p\u003e\n\n\u003cp\u003eWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"description": "SearchDomainsConfig represents search domains configuration."
},
"network.StaticHostConfigV1Alpha1": {
"properties": {
"apiVersion": {
@ -2438,6 +2557,64 @@
],
"description": "StaticHostConfig is a config document to set /etc/hosts entries."
},
"network.TimeSyncConfigV1Alpha1": {
"properties": {
"apiVersion": {
"enum": [
"v1alpha1"
],
"title": "apiVersion",
"description": "apiVersion is the API version of the resource.\n",
"markdownDescription": "apiVersion is the API version of the resource.",
"x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n"
},
"kind": {
"enum": [
"TimeSyncConfig"
],
"title": "kind",
"description": "kind is the kind of the resource.\n",
"markdownDescription": "kind is the kind of the resource.",
"x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n"
},
"enabled": {
"type": "boolean",
"title": "enabled",
"description": "Indicates if the time synchronization is enabled for the machine.\nDefaults to true.\n",
"markdownDescription": "Indicates if the time synchronization is enabled for the machine.\nDefaults to `true`.",
"x-intellij-html-description": "\u003cp\u003eIndicates if the time synchronization is enabled for the machine.\nDefaults to \u003ccode\u003etrue\u003c/code\u003e.\u003c/p\u003e\n"
},
"bootTimeout": {
"type": "string",
"pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$",
"title": "bootTimeout",
"description": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to “infinity” (waiting forever for time sync)\n",
"markdownDescription": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)",
"x-intellij-html-description": "\u003cp\u003eSpecifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \u0026ldquo;infinity\u0026rdquo; (waiting forever for time sync)\u003c/p\u003e\n"
},
"ntp": {
"$ref": "#/$defs/network.NTPConfig",
"title": "ntp",
"description": "Specifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.\n",
"markdownDescription": "Specifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.",
"x-intellij-html-description": "\u003cp\u003eSpecifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.\u003c/p\u003e\n"
},
"ptp": {
"$ref": "#/$defs/network.PTPConfig",
"title": "ptp",
"description": "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.\n",
"markdownDescription": "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.",
"x-intellij-html-description": "\u003cp\u003eSpecific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"apiVersion",
"kind"
],
"description": "TimeSyncConfig is a config document to configure time synchronization (NTP)."
},
"network.VLANConfigV1Alpha1": {
"properties": {
"apiVersion": {
@ -4412,13 +4589,6 @@
"markdownDescription": "The `env` field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.",
"x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\u003c/p\u003e\n"
},
"time": {
"$ref": "#/$defs/v1alpha1.TimeConfig",
"title": "time",
"description": "Used to configure the machines time settings.\n",
"markdownDescription": "Used to configure the machine's time settings.",
"x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s time settings.\u003c/p\u003e\n"
},
"sysctls": {
"patternProperties": {
".*": {
@ -4640,39 +4810,12 @@
},
"v1alpha1.NetworkConfig": {
"properties": {
"nameservers": {
"items": {
"type": "string"
},
"type": "array",
"title": "nameservers",
"description": "Used to statically set the nameservers for the machine.\nDefaults to 1.1.1.1 and 8.8.8.8\n",
"markdownDescription": "Used to statically set the nameservers for the machine.\nDefaults to `1.1.1.1` and `8.8.8.8`",
"x-intellij-html-description": "\u003cp\u003eUsed to statically set the nameservers for the machine.\nDefaults to \u003ccode\u003e1.1.1.1\u003c/code\u003e and \u003ccode\u003e8.8.8.8\u003c/code\u003e\u003c/p\u003e\n"
},
"searchDomains": {
"items": {
"type": "string"
},
"type": "array",
"title": "searchDomains",
"description": "Used to statically set arbitrary search domains.\n",
"markdownDescription": "Used to statically set arbitrary search domains.",
"x-intellij-html-description": "\u003cp\u003eUsed to statically set arbitrary search domains.\u003c/p\u003e\n"
},
"kubespan": {
"$ref": "#/$defs/v1alpha1.NetworkKubeSpan",
"title": "kubespan",
"description": "Configures KubeSpan feature.\n",
"markdownDescription": "Configures KubeSpan feature.",
"x-intellij-html-description": "\u003cp\u003eConfigures KubeSpan feature.\u003c/p\u003e\n"
},
"disableSearchDomain": {
"type": "boolean",
"title": "disableSearchDomain",
"description": "Disable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to false.\n",
"markdownDescription": "Disable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to `false`.",
"x-intellij-html-description": "\u003cp\u003eDisable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
@ -4886,38 +5029,6 @@
"type": "object",
"description": "SchedulerConfig represents the kube scheduler configuration options."
},
"v1alpha1.TimeConfig": {
"properties": {
"disabled": {
"type": "boolean",
"title": "disabled",
"description": "Indicates if the time service is disabled for the machine.\nDefaults to false.\n",
"markdownDescription": "Indicates if the time service is disabled for the machine.\nDefaults to `false`.",
"x-intellij-html-description": "\u003cp\u003eIndicates if the time service is disabled for the machine.\nDefaults to \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n"
},
"servers": {
"items": {
"type": "string"
},
"type": "array",
"title": "servers",
"description": "description: |\n Specifies time (NTP) servers to use for setting the system time.\n Defaults to time.cloudflare.com.\n\nTalos can also sync to the PTP time source (e.g provided by the hypervisor),\n provide the path to the PTP device as “/dev/ptp0” or “/dev/ptp_kvm”.\n",
"markdownDescription": "description: |\n Specifies time (NTP) servers to use for setting the system time.\n Defaults to `time.cloudflare.com`.\n\n Talos can also sync to the PTP time source (e.g provided by the hypervisor),\n provide the path to the PTP device as \"/dev/ptp0\" or \"/dev/ptp_kvm\".",
"x-intellij-html-description": "\u003cp\u003edescription: |\n Specifies time (NTP) servers to use for setting the system time.\n Defaults to \u003ccode\u003etime.cloudflare.com\u003c/code\u003e.\u003c/p\u003e\n\n\u003cp\u003eTalos can also sync to the PTP time source (e.g provided by the hypervisor),\n provide the path to the PTP device as \u0026ldquo;/dev/ptp0\u0026rdquo; or \u0026ldquo;/dev/ptp_kvm\u0026rdquo;.\u003c/p\u003e\n"
},
"bootTimeout": {
"type": "string",
"pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$",
"title": "bootTimeout",
"description": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to “infinity” (waiting forever for time sync)\n",
"markdownDescription": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)",
"x-intellij-html-description": "\u003cp\u003eSpecifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \u0026ldquo;infinity\u0026rdquo; (waiting forever for time sync)\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"description": "TimeConfig represents the options for configuring time on a machine."
},
"v1alpha1.UdevConfig": {
"properties": {
"rules": {
@ -5037,12 +5148,18 @@
{
"$ref": "#/$defs/network.LinkAliasConfigV1Alpha1"
},
{
"$ref": "#/$defs/network.ResolverConfigV1Alpha1"
},
{
"$ref": "#/$defs/network.RuleConfigV1Alpha1"
},
{
"$ref": "#/$defs/network.StaticHostConfigV1Alpha1"
},
{
"$ref": "#/$defs/network.TimeSyncConfigV1Alpha1"
},
{
"$ref": "#/$defs/network.VLANConfigV1Alpha1"
},

View File

@ -2,7 +2,7 @@
// 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/.
// Code generated by "deep-copy -type BondConfigV1Alpha1 -type BridgeConfigV1Alpha1 -type DefaultActionConfigV1Alpha1 -type DHCPv4ConfigV1Alpha1 -type DHCPv6ConfigV1Alpha1 -type DummyLinkConfigV1Alpha1 -type EthernetConfigV1Alpha1 -type HCloudVIPConfigV1Alpha1 -type HostnameConfigV1Alpha1 -type KubespanEndpointsConfigV1Alpha1 -type Layer2VIPConfigV1Alpha1 -type LinkConfigV1Alpha1 -type LinkAliasConfigV1Alpha1 -type RuleConfigV1Alpha1 -type StaticHostConfigV1Alpha1 -type VLANConfigV1Alpha1 -type WireguardConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
// Code generated by "deep-copy -type BondConfigV1Alpha1 -type BridgeConfigV1Alpha1 -type DefaultActionConfigV1Alpha1 -type DHCPv4ConfigV1Alpha1 -type DHCPv6ConfigV1Alpha1 -type DummyLinkConfigV1Alpha1 -type EthernetConfigV1Alpha1 -type HCloudVIPConfigV1Alpha1 -type HostnameConfigV1Alpha1 -type KubespanEndpointsConfigV1Alpha1 -type Layer2VIPConfigV1Alpha1 -type LinkConfigV1Alpha1 -type LinkAliasConfigV1Alpha1 -type ResolverConfigV1Alpha1 -type RuleConfigV1Alpha1 -type StaticHostConfigV1Alpha1 -type TimeSyncConfigV1Alpha1 -type VLANConfigV1Alpha1 -type WireguardConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
package network
@ -404,6 +404,24 @@ func (o *LinkAliasConfigV1Alpha1) DeepCopy() *LinkAliasConfigV1Alpha1 {
return &cp
}
// DeepCopy generates a deep copy of *ResolverConfigV1Alpha1.
func (o *ResolverConfigV1Alpha1) DeepCopy() *ResolverConfigV1Alpha1 {
var cp ResolverConfigV1Alpha1 = *o
if o.ResolverNameservers != nil {
cp.ResolverNameservers = make([]NameserverConfig, len(o.ResolverNameservers))
copy(cp.ResolverNameservers, o.ResolverNameservers)
}
if o.ResolverSearchDomains.SearchDomains != nil {
cp.ResolverSearchDomains.SearchDomains = make([]string, len(o.ResolverSearchDomains.SearchDomains))
copy(cp.ResolverSearchDomains.SearchDomains, o.ResolverSearchDomains.SearchDomains)
}
if o.ResolverSearchDomains.SearchDisableDefault != nil {
cp.ResolverSearchDomains.SearchDisableDefault = new(bool)
*cp.ResolverSearchDomains.SearchDisableDefault = *o.ResolverSearchDomains.SearchDisableDefault
}
return &cp
}
// DeepCopy generates a deep copy of *RuleConfigV1Alpha1.
func (o *RuleConfigV1Alpha1) DeepCopy() *RuleConfigV1Alpha1 {
var cp RuleConfigV1Alpha1 = *o
@ -428,6 +446,32 @@ func (o *StaticHostConfigV1Alpha1) DeepCopy() *StaticHostConfigV1Alpha1 {
return &cp
}
// DeepCopy generates a deep copy of *TimeSyncConfigV1Alpha1.
func (o *TimeSyncConfigV1Alpha1) DeepCopy() *TimeSyncConfigV1Alpha1 {
var cp TimeSyncConfigV1Alpha1 = *o
if o.TimeEnabled != nil {
cp.TimeEnabled = new(bool)
*cp.TimeEnabled = *o.TimeEnabled
}
if o.TimeNTP != nil {
cp.TimeNTP = new(NTPConfig)
*cp.TimeNTP = *o.TimeNTP
if o.TimeNTP.Servers != nil {
cp.TimeNTP.Servers = make([]string, len(o.TimeNTP.Servers))
copy(cp.TimeNTP.Servers, o.TimeNTP.Servers)
}
}
if o.TimePTP != nil {
cp.TimePTP = new(PTPConfig)
*cp.TimePTP = *o.TimePTP
if o.TimePTP.Devices != nil {
cp.TimePTP.Devices = make([]string, len(o.TimePTP.Devices))
copy(cp.TimePTP.Devices, o.TimePTP.Devices)
}
}
return &cp
}
// DeepCopy generates a deep copy of *VLANConfigV1Alpha1.
func (o *VLANConfigV1Alpha1) DeepCopy() *VLANConfigV1Alpha1 {
var cp VLANConfigV1Alpha1 = *o

View File

@ -154,7 +154,7 @@ func TestHostnameConfigValidate(t *testing.T) {
}
}
func TestV1Alpha1Validate(t *testing.T) {
func TestHostnameV1Alpha1Validate(t *testing.T) {
t.Parallel()
for _, test := range []struct {

View File

@ -5,6 +5,6 @@
// Package network provides network machine configuration documents.
package network
//go:generate go tool github.com/siderolabs/talos/tools/docgen -output network_doc.go network.go bond.go bridge.go default_action_config.go dhcp4.go dhcp6.go dummy.go ethernet.go hcloud_vip.go hostname.go kubespan_endpoints.go layer2_vip.go link.go link_alias.go port_range.go rule_config.go static_host.go vlan.go wireguard.go
//go:generate go tool github.com/siderolabs/talos/tools/docgen -output network_doc.go network.go bond.go bridge.go default_action_config.go dhcp4.go dhcp6.go dummy.go ethernet.go hcloud_vip.go hostname.go kubespan_endpoints.go layer2_vip.go link.go link_alias.go port_range.go resolver.go rule_config.go static_host.go time_sync.go vlan.go wireguard.go
//go:generate go tool github.com/siderolabs/deep-copy -type BondConfigV1Alpha1 -type BridgeConfigV1Alpha1 -type DefaultActionConfigV1Alpha1 -type DHCPv4ConfigV1Alpha1 -type DHCPv6ConfigV1Alpha1 -type DummyLinkConfigV1Alpha1 -type EthernetConfigV1Alpha1 -type HCloudVIPConfigV1Alpha1 -type HostnameConfigV1Alpha1 -type KubespanEndpointsConfigV1Alpha1 -type Layer2VIPConfigV1Alpha1 -type LinkConfigV1Alpha1 -type LinkAliasConfigV1Alpha1 -type RuleConfigV1Alpha1 -type StaticHostConfigV1Alpha1 -type VLANConfigV1Alpha1 -type WireguardConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go .
//go:generate go tool github.com/siderolabs/deep-copy -type BondConfigV1Alpha1 -type BridgeConfigV1Alpha1 -type DefaultActionConfigV1Alpha1 -type DHCPv4ConfigV1Alpha1 -type DHCPv6ConfigV1Alpha1 -type DummyLinkConfigV1Alpha1 -type EthernetConfigV1Alpha1 -type HCloudVIPConfigV1Alpha1 -type HostnameConfigV1Alpha1 -type KubespanEndpointsConfigV1Alpha1 -type Layer2VIPConfigV1Alpha1 -type LinkConfigV1Alpha1 -type LinkAliasConfigV1Alpha1 -type ResolverConfigV1Alpha1 -type RuleConfigV1Alpha1 -type StaticHostConfigV1Alpha1 -type TimeSyncConfigV1Alpha1 -type VLANConfigV1Alpha1 -type WireguardConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go .

View File

@ -1227,6 +1227,99 @@ func (LinkSelector) Doc() *encoder.Doc {
return doc
}
func (ResolverConfigV1Alpha1) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "ResolverConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "ResolverConfig is a config document to configure DNS resolving." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "ResolverConfig is a config document to configure DNS resolving.",
Fields: []encoder.Doc{
{
Type: "Meta",
Inline: true,
},
{
Name: "nameservers",
Type: "[]NameserverConfig",
Note: "",
Description: "A list of nameservers (DNS servers) to use for resolving domain names.\n\nNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\n\nThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.",
Comments: [3]string{"" /* encoder.HeadComment */, "A list of nameservers (DNS servers) to use for resolving domain names." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "searchDomains",
Type: "SearchDomainsConfig",
Note: "",
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 */},
},
},
}
doc.AddExample("", exampleResolverConfigV1Alpha1())
doc.AddExample("", exampleResolverConfigV1Alpha2())
return doc
}
func (NameserverConfig) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "NameserverConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "NameserverConfig represents a single nameserver configuration." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "NameserverConfig represents a single nameserver configuration.",
AppearsIn: []encoder.Appearance{
{
TypeName: "ResolverConfigV1Alpha1",
FieldName: "nameservers",
},
},
Fields: []encoder.Doc{
{
Name: "address",
Type: "Addr",
Note: "",
Description: "The IP address of the nameserver.",
Comments: [3]string{"" /* encoder.HeadComment */, "The IP address of the nameserver." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
doc.Fields[0].AddExample("", Addr{netip.MustParseAddr("10.0.0.1")})
return doc
}
func (SearchDomainsConfig) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "SearchDomainsConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "SearchDomainsConfig represents search domains configuration." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "SearchDomainsConfig represents search domains configuration.",
AppearsIn: []encoder.Appearance{
{
TypeName: "ResolverConfigV1Alpha1",
FieldName: "searchDomains",
},
},
Fields: []encoder.Doc{
{
Name: "domains",
Type: "[]string",
Note: "",
Description: "A list of search domains to be used for DNS resolution.\n\nSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if \"example.com\" is a search domain and a user tries to resolve\n\"host\", the system will attempt to resolve \"host.example.com\".\n\nThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.",
Comments: [3]string{"" /* encoder.HeadComment */, "A list of search domains to be used for DNS resolution." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "disableDefault",
Type: "bool",
Note: "",
Description: "Disable default search domain configuration from hostname FQDN.\n\nWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.",
Comments: [3]string{"" /* encoder.HeadComment */, "Disable default search domain configuration from hostname FQDN." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
return doc
}
func (RuleConfigV1Alpha1) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "NetworkRuleConfig",
@ -1375,6 +1468,104 @@ func (StaticHostConfigV1Alpha1) Doc() *encoder.Doc {
return doc
}
func (TimeSyncConfigV1Alpha1) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "TimeSyncConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "TimeSyncConfig is a config document to configure time synchronization (NTP)." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "TimeSyncConfig is a config document to configure time synchronization (NTP).",
Fields: []encoder.Doc{
{
Type: "Meta",
Inline: true,
},
{
Name: "enabled",
Type: "bool",
Note: "",
Description: "Indicates if the time synchronization is enabled for the machine.\nDefaults to `true`.",
Comments: [3]string{"" /* encoder.HeadComment */, "Indicates if the time synchronization is enabled for the machine." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "bootTimeout",
Type: "Duration",
Note: "",
Description: "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)",
Comments: [3]string{"" /* encoder.HeadComment */, "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "ntp",
Type: "NTPConfig",
Note: "",
Description: "Specifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.",
Comments: [3]string{"" /* encoder.HeadComment */, "Specifies NTP configuration to sync the time over network." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "ptp",
Type: "PTPConfig",
Note: "",
Description: "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.",
Comments: [3]string{"" /* encoder.HeadComment */, "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
doc.AddExample("", exampleTimeSyncConfigV1Alpha1())
doc.AddExample("", exampleTimeSyncConfigV1Alpha2())
return doc
}
func (NTPConfig) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "NTPConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "NTPConfig represents a NTP server configuration." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "NTPConfig represents a NTP server configuration.",
AppearsIn: []encoder.Appearance{
{
TypeName: "TimeSyncConfigV1Alpha1",
FieldName: "ntp",
},
},
Fields: []encoder.Doc{
{
Name: "servers",
Type: "[]string",
Note: "",
Description: "Specifies time (NTP) servers to use for setting the system time.\nDefaults to `time.cloudflare.com`.",
Comments: [3]string{"" /* encoder.HeadComment */, "Specifies time (NTP) servers to use for setting the system time." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
return doc
}
func (PTPConfig) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "PTPConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "PTPConfig represents a PTP (Precision Time Protocol) configuration." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "PTPConfig represents a PTP (Precision Time Protocol) configuration.",
AppearsIn: []encoder.Appearance{
{
TypeName: "TimeSyncConfigV1Alpha1",
FieldName: "ptp",
},
},
Fields: []encoder.Doc{
{
Name: "devices",
Type: "[]string",
Note: "",
Description: "description: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\n\n A PTP device is typically represented as a character device file in the /dev directory,\n such as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.\n",
Comments: [3]string{"" /* encoder.HeadComment */, "description: |" /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
return doc
}
func (VLANConfigV1Alpha1) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "VLANConfig",
@ -1573,10 +1764,16 @@ func GetFileDoc() *encoder.FileDoc {
RouteConfig{}.Doc(),
LinkAliasConfigV1Alpha1{}.Doc(),
LinkSelector{}.Doc(),
ResolverConfigV1Alpha1{}.Doc(),
NameserverConfig{}.Doc(),
SearchDomainsConfig{}.Doc(),
RuleConfigV1Alpha1{}.Doc(),
RulePortSelector{}.Doc(),
IngressRule{}.Doc(),
StaticHostConfigV1Alpha1{}.Doc(),
TimeSyncConfigV1Alpha1{}.Doc(),
NTPConfig{}.Doc(),
PTPConfig{}.Doc(),
VLANConfigV1Alpha1{}.Doc(),
WireguardConfigV1Alpha1{}.Doc(),
WireguardPeer{}.Doc(),

View File

@ -0,0 +1,177 @@
// 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
//docgen:jsonschema
import (
"errors"
"net/netip"
"slices"
"github.com/siderolabs/gen/xslices"
"github.com/siderolabs/go-pointer"
"github.com/siderolabs/talos/pkg/machinery/config/config"
"github.com/siderolabs/talos/pkg/machinery/config/container"
"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"
)
// ResolverKind is a ResolverConfig document kind.
const ResolverKind = "ResolverConfig"
func init() {
registry.Register(ResolverKind, func(version string) config.Document {
switch version {
case "v1alpha1": //nolint:goconst
return &ResolverConfigV1Alpha1{}
default:
return nil
}
})
}
// Check interfaces.
var (
_ config.NetworkResolverConfig = &ResolverConfigV1Alpha1{}
_ container.V1Alpha1ConflictValidator = &ResolverConfigV1Alpha1{}
)
// ResolverConfigV1Alpha1 is a config document to configure DNS resolving.
//
// examples:
// - value: exampleResolverConfigV1Alpha1()
// - value: exampleResolverConfigV1Alpha2()
// alias: ResolverConfig
// schemaRoot: true
// schemaMeta: v1alpha1/ResolverConfig
type ResolverConfigV1Alpha1 struct {
meta.Meta `yaml:",inline"`
// description: |
// A list of nameservers (DNS servers) to use for resolving domain names.
//
// Nameservers are used to resolve domain names on the host, and they are also
// propagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.
//
// This overrides any nameservers obtained via DHCP or platform configuration.
// Default configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.
ResolverNameservers []NameserverConfig `yaml:"nameservers,omitempty"`
// description: |
// Configuration for search domains (in /etc/resolv.conf).
//
// The default is to derive search domains from the hostname FQDN.
ResolverSearchDomains SearchDomainsConfig `yaml:"searchDomains,omitempty"`
}
// NameserverConfig represents a single nameserver configuration.
type NameserverConfig struct {
// description: |
// The IP address of the nameserver.
// examples:
// - value: >
// Addr{netip.MustParseAddr("10.0.0.1")}
// schema:
// type: string
// pattern: ^[0-9a-f.:]+$
Address Addr `yaml:"address"`
}
// SearchDomainsConfig represents search domains configuration.
type SearchDomainsConfig struct {
// description: |
// A list of search domains to be used for DNS resolution.
//
// Search domains are appended to unqualified domain names during DNS resolution.
// For example, if "example.com" is a search domain and a user tries to resolve
// "host", the system will attempt to resolve "host.example.com".
//
// This overrides any search domains obtained via DHCP or platform configuration.
// The default configuration derives the search domain from the hostname FQDN.
SearchDomains []string `yaml:"domains,omitempty"`
// description: |
// Disable default search domain configuration from hostname FQDN.
//
// When set to true, the system will not derive search domains from the hostname FQDN.
// This allows for a custom configuration of search domains without any defaults.
SearchDisableDefault *bool `yaml:"disableDefault,omitempty"`
}
// NewResolverConfigV1Alpha1 creates a new ResolverConfig config document.
func NewResolverConfigV1Alpha1() *ResolverConfigV1Alpha1 {
return &ResolverConfigV1Alpha1{
Meta: meta.Meta{
MetaKind: ResolverKind,
MetaAPIVersion: "v1alpha1",
},
}
}
func exampleResolverConfigV1Alpha1() *ResolverConfigV1Alpha1 {
cfg := NewResolverConfigV1Alpha1()
cfg.ResolverNameservers = []NameserverConfig{
{
Address: Addr{netip.MustParseAddr("1.1.1.1")},
},
{
Address: Addr{netip.MustParseAddr("ff08::1")},
},
}
cfg.ResolverSearchDomains = SearchDomainsConfig{
SearchDomains: []string{"example.com"},
}
return cfg
}
func exampleResolverConfigV1Alpha2() *ResolverConfigV1Alpha1 {
cfg := NewResolverConfigV1Alpha1()
cfg.ResolverSearchDomains = SearchDomainsConfig{
SearchDisableDefault: pointer.To(true),
}
return cfg
}
// Clone implements config.Document interface.
func (s *ResolverConfigV1Alpha1) Clone() config.Document {
return s.DeepCopy()
}
// V1Alpha1ConflictValidate implements container.V1Alpha1ConflictValidator interface.
func (s *ResolverConfigV1Alpha1) V1Alpha1ConflictValidate(v1alpha1Cfg *v1alpha1.Config) error {
if v1alpha1Cfg.SearchDomains() != nil {
return errors.New(".machine.network.searchDomains is already set in v1alpha1 config")
}
if v1alpha1Cfg.Resolvers() != nil {
return errors.New(".machine.network.nameservers is already set in v1alpha1 config")
}
if v1alpha1Cfg.DisableSearchDomain() {
return errors.New(".machine.network.disableSearchDomain is already set in v1alpha1 config")
}
return nil
}
// Resolvers implements NetworkResolverConfig interface.
func (s *ResolverConfigV1Alpha1) Resolvers() []netip.Addr {
return xslices.Map(s.ResolverNameservers, func(ns NameserverConfig) netip.Addr {
return ns.Address.Addr
})
}
// SearchDomains implements NetworkResolverConfig interface.
func (s *ResolverConfigV1Alpha1) SearchDomains() []string {
return slices.Clone(s.ResolverSearchDomains.SearchDomains)
}
// DisableSearchDomain implements NetworkResolverConfig interface.
func (s *ResolverConfigV1Alpha1) DisableSearchDomain() bool {
return pointer.SafeDeref(s.ResolverSearchDomains.SearchDisableDefault)
}

View File

@ -0,0 +1,146 @@
// 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 (
_ "embed"
"net/netip"
"testing"
"github.com/siderolabs/go-pointer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/siderolabs/talos/pkg/machinery/config/configloader"
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
"github.com/siderolabs/talos/pkg/machinery/config/types/network"
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
)
//go:embed testdata/resolverconfig.yaml
var expectedResolverConfigDocument []byte
func TestResolverConfigMarshalStability(t *testing.T) {
t.Parallel()
cfg := network.NewResolverConfigV1Alpha1()
cfg.ResolverNameservers = []network.NameserverConfig{
{
Address: network.Addr{Addr: netip.MustParseAddr("10.0.0.1")},
},
{
Address: network.Addr{Addr: netip.MustParseAddr("2001:4860:4860::8888")},
},
}
cfg.ResolverSearchDomains = network.SearchDomainsConfig{
SearchDomains: []string{"example.org", "example.com"},
SearchDisableDefault: pointer.To(false),
}
marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode()
require.NoError(t, err)
t.Log(string(marshaled))
assert.Equal(t, expectedResolverConfigDocument, marshaled)
}
func TestResolverConfigUnmarshal(t *testing.T) {
t.Parallel()
provider, err := configloader.NewFromBytes(expectedResolverConfigDocument)
require.NoError(t, err)
docs := provider.Documents()
require.Len(t, docs, 1)
assert.Equal(t, &network.ResolverConfigV1Alpha1{
Meta: meta.Meta{
MetaAPIVersion: "v1alpha1",
MetaKind: network.ResolverKind,
},
ResolverNameservers: []network.NameserverConfig{
{
Address: network.Addr{Addr: netip.MustParseAddr("10.0.0.1")},
},
{
Address: network.Addr{Addr: netip.MustParseAddr("2001:4860:4860::8888")},
},
},
ResolverSearchDomains: network.SearchDomainsConfig{
SearchDomains: []string{"example.org", "example.com"},
SearchDisableDefault: pointer.To(false),
},
}, docs[0])
}
func TestResolverV1Alpha1Validate(t *testing.T) {
t.Parallel()
for _, test := range []struct {
name string
v1alpha1Cfg *v1alpha1.Config
cfg func() *network.ResolverConfigV1Alpha1
expectedError string
}{
{
name: "empty",
v1alpha1Cfg: &v1alpha1.Config{},
cfg: network.NewResolverConfigV1Alpha1,
},
{
name: "v1alpha1 nameservers set",
v1alpha1Cfg: &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{
NameServers: []string{"1.1.1.1"},
},
},
},
cfg: network.NewResolverConfigV1Alpha1,
expectedError: ".machine.network.nameservers is already set in v1alpha1 config",
},
{
name: "v1alpha1 search domains set",
v1alpha1Cfg: &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{
Searches: []string{"cluster.org"},
},
},
},
cfg: network.NewResolverConfigV1Alpha1,
expectedError: ".machine.network.searchDomains is already set in v1alpha1 config",
},
{
name: "v1alpha1 disable search domains set",
v1alpha1Cfg: &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{
NetworkDisableSearchDomain: pointer.To(true),
},
},
},
cfg: network.NewResolverConfigV1Alpha1,
expectedError: ".machine.network.disableSearchDomain is already set in v1alpha1 config",
},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
err := test.cfg().V1Alpha1ConflictValidate(test.v1alpha1Cfg)
if test.expectedError != "" {
assert.EqualError(t, err, test.expectedError)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@ -0,0 +1,10 @@
apiVersion: v1alpha1
kind: ResolverConfig
nameservers:
- address: 10.0.0.1
- address: 2001:4860:4860::8888
searchDomains:
domains:
- example.org
- example.com
disableDefault: false

View File

@ -0,0 +1,7 @@
apiVersion: v1alpha1
kind: TimeSyncConfig
enabled: true
bootTimeout: 1m0s
ntp:
servers:
- time.cloudflare.com

View File

@ -0,0 +1,191 @@
// 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
//docgen:jsonschema
import (
"errors"
"time"
"github.com/siderolabs/talos/pkg/machinery/config/config"
"github.com/siderolabs/talos/pkg/machinery/config/container"
"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"
)
// TimeSyncKind is a TimeSyncConfig document kind.
const TimeSyncKind = "TimeSyncConfig"
func init() {
registry.Register(TimeSyncKind, func(version string) config.Document {
switch version {
case "v1alpha1": //nolint:goconst
return &TimeSyncConfigV1Alpha1{}
default:
return nil
}
})
}
// Check interfaces.
var (
_ config.NetworkTimeSyncConfig = &TimeSyncConfigV1Alpha1{}
_ config.Validator = &TimeSyncConfigV1Alpha1{}
_ container.V1Alpha1ConflictValidator = &TimeSyncConfigV1Alpha1{}
)
// TimeSyncConfigV1Alpha1 is a config document to configure time synchronization (NTP).
//
// examples:
// - value: exampleTimeSyncConfigV1Alpha1()
// - value: exampleTimeSyncConfigV1Alpha2()
// alias: TimeSyncConfig
// schemaRoot: true
// schemaMeta: v1alpha1/TimeSyncConfig
type TimeSyncConfigV1Alpha1 struct {
meta.Meta `yaml:",inline"`
// description: |
// Indicates if the time synchronization is enabled for the machine.
// Defaults to `true`.
TimeEnabled *bool `yaml:"enabled,omitempty"`
// description: |
// Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.
// NTP sync will be still running in the background.
// Defaults to "infinity" (waiting forever for time sync)
// schema:
// type: string
// pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$
TimeBootTimeout time.Duration `yaml:"bootTimeout,omitempty"`
// description: |
// Specifies NTP configuration to sync the time over network.
// Mutually exclusive with PTP configuration.
TimeNTP *NTPConfig `yaml:"ntp,omitempty"`
// description: |
// Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.
// Mutually exclusive with NTP configuration.
TimePTP *PTPConfig `yaml:"ptp,omitempty"`
}
// NTPConfig represents a NTP server configuration.
type NTPConfig struct {
// description: |
// Specifies time (NTP) servers to use for setting the system time.
// Defaults to `time.cloudflare.com`.
Servers []string `yaml:"servers,omitempty"`
}
// PTPConfig represents a PTP (Precision Time Protocol) configuration.
type PTPConfig struct {
// description: |
// A list of PTP devices to sync with (e.g. provided by the hypervisor).
//
// A PTP device is typically represented as a character device file in the /dev directory,
// such as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time
// with an external time source that supports the Precision Time Protocol.
Devices []string `yaml:"devices,omitempty"`
}
// NewTimeSyncConfigV1Alpha1 creates a new TimeSyncConfig config document.
func NewTimeSyncConfigV1Alpha1() *TimeSyncConfigV1Alpha1 {
return &TimeSyncConfigV1Alpha1{
Meta: meta.Meta{
MetaKind: TimeSyncKind,
MetaAPIVersion: "v1alpha1",
},
}
}
func exampleTimeSyncConfigV1Alpha1() *TimeSyncConfigV1Alpha1 {
cfg := NewTimeSyncConfigV1Alpha1()
cfg.TimeNTP = &NTPConfig{
Servers: []string{"pool.ntp.org"},
}
return cfg
}
func exampleTimeSyncConfigV1Alpha2() *TimeSyncConfigV1Alpha1 {
cfg := NewTimeSyncConfigV1Alpha1()
cfg.TimePTP = &PTPConfig{
Devices: []string{"/dev/ptp0"},
}
return cfg
}
// Clone implements config.Document interface.
func (s *TimeSyncConfigV1Alpha1) Clone() config.Document {
return s.DeepCopy()
}
// Validate implements config.Validator interface.
func (s *TimeSyncConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) {
var errs error
if s.TimeBootTimeout < 0 {
errs = errors.Join(errs, errors.New("bootTimeout cannot be negative"))
}
if s.TimeNTP != nil && s.TimePTP != nil {
errs = errors.Join(errs, errors.New("only one of ntp or ptp configuration can be specified"))
}
return nil, errs
}
// V1Alpha1ConflictValidate implements container.V1Alpha1ConflictValidator interface.
func (s *TimeSyncConfigV1Alpha1) V1Alpha1ConflictValidate(v1alpha1Cfg *v1alpha1.Config) error {
v1tsc := v1alpha1Cfg.NetworkTimeSyncConfig()
if v1tsc == nil {
return nil
}
if v1tsc.Disabled() {
return errors.New("time sync cannot be disabled in both v1alpha1 and new-style configuration")
}
if len(v1tsc.Servers()) > 0 {
return errors.New("time servers cannot be specified in both v1alpha1 and new-style configuration")
}
if v1tsc.BootTimeout() != 0 {
return errors.New("boot timeout cannot be specified in both v1alpha1 and new-style configuration")
}
return nil
}
// Disabled implements config.NetworkTimeSyncConfig interface.
func (s *TimeSyncConfigV1Alpha1) Disabled() bool {
if s.TimeEnabled == nil {
return false
}
return !*s.TimeEnabled
}
// BootTimeout implements config.NetworkTimeSyncConfig interface.
func (s *TimeSyncConfigV1Alpha1) BootTimeout() time.Duration {
return s.TimeBootTimeout
}
// Servers implements config.NetworkTimeSyncConfig interface.
func (s *TimeSyncConfigV1Alpha1) Servers() []string {
// The configuration validates that only one of the NTP or PTP is set.
if s.TimeNTP != nil {
return s.TimeNTP.Servers
}
if s.TimePTP != nil {
return s.TimePTP.Devices
}
return nil
}

View File

@ -0,0 +1,200 @@
// 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 (
_ "embed"
"testing"
"time"
"github.com/siderolabs/go-pointer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/siderolabs/talos/pkg/machinery/config/configloader"
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
"github.com/siderolabs/talos/pkg/machinery/config/types/network"
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
)
//go:embed testdata/timesyncconfig.yaml
var expectedTimeSyncConfigDocument []byte
func TestTimeSyncConfigMarshalStability(t *testing.T) {
t.Parallel()
cfg := network.NewTimeSyncConfigV1Alpha1()
cfg.TimeEnabled = pointer.To(true)
cfg.TimeBootTimeout = time.Minute
cfg.TimeNTP = &network.NTPConfig{
Servers: []string{"time.cloudflare.com"},
}
marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode()
require.NoError(t, err)
t.Log(string(marshaled))
assert.Equal(t, expectedTimeSyncConfigDocument, marshaled)
}
func TestTimeSyncConfigUnmarshal(t *testing.T) {
t.Parallel()
provider, err := configloader.NewFromBytes(expectedTimeSyncConfigDocument)
require.NoError(t, err)
docs := provider.Documents()
require.Len(t, docs, 1)
assert.Equal(t, &network.TimeSyncConfigV1Alpha1{
Meta: meta.Meta{
MetaAPIVersion: "v1alpha1",
MetaKind: network.TimeSyncKind,
},
TimeEnabled: pointer.To(true),
TimeBootTimeout: time.Minute,
TimeNTP: &network.NTPConfig{
Servers: []string{"time.cloudflare.com"},
},
}, docs[0])
}
func TestTimeSyncValidate(t *testing.T) {
t.Parallel()
for _, test := range []struct {
name string
cfg func() *network.TimeSyncConfigV1Alpha1
expectedError string
expectedWarnings []string
}{
{
name: "empty",
cfg: network.NewTimeSyncConfigV1Alpha1,
},
{
name: "both NTP and PTP set",
cfg: func() *network.TimeSyncConfigV1Alpha1 {
cfg := network.NewTimeSyncConfigV1Alpha1()
cfg.TimeNTP = &network.NTPConfig{
Servers: []string{"pool.ntp.org"},
}
cfg.TimePTP = &network.PTPConfig{
Devices: []string{"/dev/ptp0"},
}
return cfg
},
expectedError: "only one of ntp or ptp configuration can be specified",
},
{
name: "negative boot timeout",
cfg: func() *network.TimeSyncConfigV1Alpha1 {
cfg := network.NewTimeSyncConfigV1Alpha1()
cfg.TimeBootTimeout = -time.Second
return cfg
},
expectedError: "bootTimeout cannot be negative",
},
{
name: "valid NTP config",
cfg: func() *network.TimeSyncConfigV1Alpha1 {
cfg := network.NewTimeSyncConfigV1Alpha1()
cfg.TimeNTP = &network.NTPConfig{
Servers: []string{"pool.ntp.org"},
}
cfg.TimeBootTimeout = time.Second
return cfg
},
},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
warnings, err := test.cfg().Validate(validationMode{})
assert.Equal(t, test.expectedWarnings, warnings)
if test.expectedError != "" {
assert.EqualError(t, err, test.expectedError)
} else {
assert.NoError(t, err)
}
})
}
}
func TestTimeSyncV1Alpha1Validate(t *testing.T) {
t.Parallel()
for _, test := range []struct {
name string
v1alpha1Cfg *v1alpha1.Config
cfg func() *network.TimeSyncConfigV1Alpha1
expectedError string
}{
{
name: "empty",
v1alpha1Cfg: &v1alpha1.Config{},
cfg: network.NewTimeSyncConfigV1Alpha1,
},
{
name: "v1alpha1 timeservers set",
v1alpha1Cfg: &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineTime: &v1alpha1.TimeConfig{
TimeServers: []string{"za.pool.ntp.org"},
},
},
},
cfg: network.NewTimeSyncConfigV1Alpha1,
expectedError: "time servers cannot be specified in both v1alpha1 and new-style configuration",
},
{
name: "v1alpha1 boot timeout set",
v1alpha1Cfg: &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineTime: &v1alpha1.TimeConfig{
TimeBootTimeout: time.Second,
},
},
},
cfg: network.NewTimeSyncConfigV1Alpha1,
expectedError: "boot timeout cannot be specified in both v1alpha1 and new-style configuration",
},
{
name: "v1alpha1 disable set",
v1alpha1Cfg: &v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineTime: &v1alpha1.TimeConfig{
TimeDisabled: pointer.To(true),
},
},
},
cfg: network.NewTimeSyncConfigV1Alpha1,
expectedError: "time sync cannot be disabled in both v1alpha1 and new-style configuration",
},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
err := test.cfg().V1Alpha1ConflictValidate(test.v1alpha1Cfg)
if test.expectedError != "" {
assert.EqualError(t, err, test.expectedError)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@ -161,14 +161,6 @@ func machineEnvExamples2() Env {
}
}
func machineTimeExample() *TimeConfig {
return &TimeConfig{
TimeDisabled: pointer.To(false),
TimeServers: []string{"time.cloudflare.com"},
TimeBootTimeout: 2 * time.Minute,
}
}
func machineSysctlsExample() map[string]string {
return map[string]string{
"kernel.domainname": "talos.dev",

View File

@ -5,6 +5,8 @@
package v1alpha1
import (
"net/netip"
"github.com/siderolabs/go-pointer"
"github.com/siderolabs/talos/pkg/machinery/config/config"
@ -44,3 +46,47 @@ func (c *Config) AutoHostname() nethelpers.AutoHostnameKind {
return nethelpers.AutoHostnameKindAddr
}
// Resolvers implements config.NetworkResolverConfig interface.
func (c *Config) Resolvers() []netip.Addr {
if c.MachineConfig == nil || c.MachineConfig.MachineNetwork == nil {
return nil
}
var result []netip.Addr
for _, r := range c.MachineConfig.MachineNetwork.NameServers {
if addr, err := netip.ParseAddr(r); err == nil {
result = append(result, addr)
}
}
return result
}
// SearchDomains implements config.NetworkResolverConfig interface.
func (c *Config) SearchDomains() []string {
if c.MachineConfig == nil || c.MachineConfig.MachineNetwork == nil {
return nil
}
return c.MachineConfig.MachineNetwork.Searches
}
// DisableSearchDomain implements config.NetworkResolverConfig interface.
func (c *Config) DisableSearchDomain() bool {
if c.MachineConfig == nil || c.MachineConfig.MachineNetwork == nil {
return false
}
return pointer.SafeDeref(c.MachineConfig.MachineNetwork.NetworkDisableSearchDomain)
}
// NetworkTimeSyncConfig implements config.NetworkTimeSyncConfig interface.
func (c *Config) NetworkTimeSyncConfig() config.NetworkTimeSyncConfig {
if c.MachineConfig == nil || c.MachineConfig.MachineTime == nil {
return nil
}
return c.MachineConfig.MachineTime
}

View File

@ -5,7 +5,9 @@
package v1alpha1_test
import (
"net/netip"
"testing"
"time"
"github.com/siderolabs/go-pointer"
"github.com/stretchr/testify/assert"
@ -238,3 +240,240 @@ func TestHostnameBridging(t *testing.T) {
})
}
}
func TestResolverBridging(t *testing.T) {
t.Parallel()
for _, test := range []struct {
name string
cfg func(*testing.T) config.Config
expectedNameservers []netip.Addr
expectedSearchDomains []string
expectedDisableSearch bool
}{
{
name: "v1alpha1 only",
cfg: func(*testing.T) config.Config {
return container.NewV1Alpha1(&v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{
NameServers: []string{"2.2.2.2", "3.3.3.3"},
Searches: []string{"universe.com", "galaxy.org"},
NetworkDisableSearchDomain: pointer.To(true),
},
},
})
},
expectedNameservers: []netip.Addr{netip.MustParseAddr("2.2.2.2"), netip.MustParseAddr("3.3.3.3")},
expectedSearchDomains: []string{"universe.com", "galaxy.org"},
expectedDisableSearch: true,
},
{
name: "v1alpha1 empty",
cfg: func(*testing.T) config.Config {
return container.NewV1Alpha1(&v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{},
})
},
expectedNameservers: nil,
expectedSearchDomains: nil,
expectedDisableSearch: false,
},
{
name: "new style only",
cfg: func(*testing.T) config.Config {
rc := network.NewResolverConfigV1Alpha1()
rc.ResolverNameservers = []network.NameserverConfig{
{
Address: network.Addr{Addr: netip.MustParseAddr("2.2.2.2")},
},
{
Address: network.Addr{Addr: netip.MustParseAddr("3.3.3.3")},
},
}
rc.ResolverSearchDomains = network.SearchDomainsConfig{
SearchDomains: []string{"universe.com", "galaxy.org"},
SearchDisableDefault: pointer.To(true),
}
c, err := container.New(
rc,
)
require.NoError(t, err)
return c
},
expectedNameservers: []netip.Addr{netip.MustParseAddr("2.2.2.2"), netip.MustParseAddr("3.3.3.3")},
expectedSearchDomains: []string{"universe.com", "galaxy.org"},
expectedDisableSearch: true,
},
{
name: "mixed",
cfg: func(*testing.T) config.Config {
rc := network.NewResolverConfigV1Alpha1()
rc.ResolverNameservers = []network.NameserverConfig{
{
Address: network.Addr{Addr: netip.MustParseAddr("2.2.2.2")},
},
{
Address: network.Addr{Addr: netip.MustParseAddr("3.3.3.3")},
},
}
c, err := container.New(
rc,
&v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{},
},
},
)
require.NoError(t, err)
return c
},
expectedNameservers: []netip.Addr{netip.MustParseAddr("2.2.2.2"), netip.MustParseAddr("3.3.3.3")},
expectedSearchDomains: nil,
expectedDisableSearch: false,
},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
cfg := test.cfg(t)
resolverConfig := cfg.NetworkResolverConfig()
require.NotNil(t, resolverConfig)
assert.Equal(t, test.expectedNameservers, resolverConfig.Resolvers())
assert.Equal(t, test.expectedSearchDomains, resolverConfig.SearchDomains())
assert.Equal(t, test.expectedDisableSearch, resolverConfig.DisableSearchDomain())
})
}
}
func TestTimeSyncBridging(t *testing.T) {
t.Parallel()
for _, test := range []struct {
name string
cfg func(*testing.T) config.Config
expectedNil bool
expectedDisabled bool
expectedTimeservers []string
expectedBootTimeout time.Duration
}{
{
name: "v1alpha1 only",
cfg: func(*testing.T) config.Config {
return container.NewV1Alpha1(&v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineTime: &v1alpha1.TimeConfig{
TimeDisabled: pointer.To(true),
TimeServers: []string{"time1.example.com", "time2.example.com"},
TimeBootTimeout: 30 * time.Second,
},
},
})
},
expectedDisabled: true,
expectedTimeservers: []string{"time1.example.com", "time2.example.com"},
expectedBootTimeout: 30 * time.Second,
},
{
name: "v1alpha1 empty",
cfg: func(*testing.T) config.Config {
return container.NewV1Alpha1(&v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{},
})
},
expectedNil: true,
},
{
name: "new style only",
cfg: func(*testing.T) config.Config {
tsc := network.NewTimeSyncConfigV1Alpha1()
tsc.TimeBootTimeout = 10 * time.Second
tsc.TimeNTP = &network.NTPConfig{
Servers: []string{"time1.example.com", "time2.example.com"},
}
c, err := container.New(
tsc,
)
require.NoError(t, err)
return c
},
expectedDisabled: false,
expectedTimeservers: []string{"time1.example.com", "time2.example.com"},
expectedBootTimeout: 10 * time.Second,
},
{
name: "mixed",
cfg: func(*testing.T) config.Config {
tsc := network.NewTimeSyncConfigV1Alpha1()
tsc.TimeBootTimeout = 10 * time.Second
tsc.TimeNTP = &network.NTPConfig{
Servers: []string{"time1.example.com", "time2.example.com"},
}
c, err := container.New(
tsc,
&v1alpha1.Config{
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{},
},
},
)
require.NoError(t, err)
return c
},
expectedDisabled: false,
expectedTimeservers: []string{"time1.example.com", "time2.example.com"},
expectedBootTimeout: 10 * time.Second,
},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
cfg := test.cfg(t)
timesyncConfig := cfg.NetworkTimeSyncConfig()
if test.expectedNil {
require.Nil(t, timesyncConfig)
return
}
require.NotNil(t, timesyncConfig)
assert.Equal(t, test.expectedTimeservers, timesyncConfig.Servers())
assert.Equal(t, test.expectedDisabled, timesyncConfig.Disabled())
assert.Equal(t, test.expectedBootTimeout, timesyncConfig.BootTimeout())
})
}
}

View File

@ -196,15 +196,6 @@ func (m *MachineConfig) Network() config.MachineNetwork {
return m.MachineNetwork
}
// Time implements the config.Provider interface.
func (m *MachineConfig) Time() config.Time {
if m.MachineTime == nil {
return &TimeConfig{}
}
return m.MachineTime
}
// Controlplane implements the config.Provider interface.
func (m *MachineConfig) Controlplane() config.MachineControlPlane {
if m.MachineControlPlane == nil {
@ -577,11 +568,6 @@ func (r *RegistryTLSConfig) InsecureSkipVerify() bool {
return pointer.SafeDeref(r.TLSInsecureSkipVerify)
}
// DisableSearchDomain implements the config.Provider interface.
func (n *NetworkConfig) DisableSearchDomain() bool {
return pointer.SafeDeref(n.NetworkDisableSearchDomain)
}
// Devices implements the config.Provider interface.
func (n *NetworkConfig) Devices() []config.Device {
return xslices.Map(n.NetworkInterfaces, func(d *Device) config.Device { return d })
@ -604,16 +590,6 @@ func (n *NetworkConfig) getDevice(iface IfaceSelector) *Device {
return dev
}
// Resolvers implements the config.Provider interface.
func (n *NetworkConfig) Resolvers() []string {
return n.NameServers
}
// SearchDomains implements the config.Provider interface.
func (n *NetworkConfig) SearchDomains() []string {
return n.Searches
}
// ExtraHosts implements the config.Provider interface.
func (n *NetworkConfig) ExtraHosts() []config.NetworkStaticHostConfig {
return xslices.Map(n.ExtraHostEntries, func(e *ExtraHost) config.NetworkStaticHostConfig { return e })

View File

@ -219,11 +219,9 @@ type MachineConfig struct {
// ".*":
// type: string
MachineEnv Env `yaml:"env,omitempty"`
// description: |
// Used to configure the machine's time settings.
// examples:
// - name: Example configuration for cloudflare ntp server.
// value: machineTimeExample()
// docgen:nodoc
//
// Deprecated: Use 'TimeSyncConfig' instead.
MachineTime *TimeConfig `yaml:"time,omitempty"`
// description: |
// Used to configure the machine's sysctls.
@ -688,16 +686,13 @@ type NetworkConfig struct {
//
// Deprecated: use multi-doc network config.
NetworkInterfaces NetworkDeviceList `yaml:"interfaces,omitempty"`
// description: |
// Used to statically set the nameservers for the machine.
// Defaults to `1.1.1.1` and `8.8.8.8`
// examples:
// - value: '[]string{"8.8.8.8", "1.1.1.1"}'
// docgen:nodoc
//
// Deprecated: Use `ResolverConfig` instead.
NameServers []string `yaml:"nameservers,omitempty"`
// description: |
// Used to statically set arbitrary search domains.
// examples:
// - value: '[]string{"example.org", "example.com"}'
// docgen:nodoc
//
// Deprecated: Use `ResolverConfig` instead.
Searches []string `yaml:"searchDomains,omitempty"`
// docgen:nodoc
//
@ -708,15 +703,9 @@ type NetworkConfig struct {
// examples:
// - value: networkKubeSpanExample()
NetworkKubeSpan *NetworkKubeSpan `yaml:"kubespan,omitempty"`
// description: |
// Disable generating a default search domain in /etc/resolv.conf
// based on the machine hostname.
// Defaults to `false`.
// values:
// - true
// - yes
// - false
// - no
// docgen:nodoc
//
// Deprecated: Use `ResolverConfig` instead.
NetworkDisableSearchDomain *bool `yaml:"disableSearchDomain,omitempty"`
}
@ -933,6 +922,8 @@ type InstallExtensionConfig struct {
}
// TimeConfig represents the options for configuring time on a machine.
//
//docgen:nodoc
type TimeConfig struct {
// description: |
// Indicates if the time service is disabled for the machine.

View File

@ -173,13 +173,7 @@ func (MachineConfig) Doc() *encoder.Doc {
"`no_proxy`",
},
},
{
Name: "time",
Type: "TimeConfig",
Note: "",
Description: "Used to configure the machine's time settings.",
Comments: [3]string{"" /* encoder.HeadComment */, "Used to configure the machine's time settings." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{},
{
Name: "sysctls",
Type: "map[string]string",
@ -276,7 +270,6 @@ func (MachineConfig) Doc() *encoder.Doc {
doc.Fields[12].AddExample("Environment variables definition examples.", machineEnvExamples0())
doc.Fields[12].AddExample("", machineEnvExamples1())
doc.Fields[12].AddExample("", machineEnvExamples2())
doc.Fields[13].AddExample("Example configuration for cloudflare ntp server.", machineTimeExample())
doc.Fields[14].AddExample("MachineSysctls usage example.", machineSysctlsExample())
doc.Fields[15].AddExample("MachineSysfs usage example.", machineSysfsExample())
doc.Fields[18].AddExample("", machineFeaturesExample())
@ -917,20 +910,8 @@ func (NetworkConfig) Doc() *encoder.Doc {
Fields: []encoder.Doc{
{},
{},
{
Name: "nameservers",
Type: "[]string",
Note: "",
Description: "Used to statically set the nameservers for the machine.\nDefaults to `1.1.1.1` and `8.8.8.8`",
Comments: [3]string{"" /* encoder.HeadComment */, "Used to statically set the nameservers for the machine." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "searchDomains",
Type: "[]string",
Note: "",
Description: "Used to statically set arbitrary search domains.",
Comments: [3]string{"" /* encoder.HeadComment */, "Used to statically set arbitrary search domains." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{},
{},
{},
{
Name: "kubespan",
@ -939,26 +920,12 @@ func (NetworkConfig) Doc() *encoder.Doc {
Description: "Configures KubeSpan feature.",
Comments: [3]string{"" /* encoder.HeadComment */, "Configures KubeSpan feature." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "disableSearchDomain",
Type: "bool",
Note: "",
Description: "Disable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to `false`.",
Comments: [3]string{"" /* encoder.HeadComment */, "Disable generating a default search domain in /etc/resolv.conf" /* encoder.LineComment */, "" /* encoder.FootComment */},
Values: []string{
"true",
"yes",
"false",
"no",
},
},
{},
},
}
doc.AddExample("Network definition example.", machineNetworkConfigExample())
doc.Fields[2].AddExample("", []string{"8.8.8.8", "1.1.1.1"})
doc.Fields[3].AddExample("", []string{"example.org", "example.com"})
doc.Fields[5].AddExample("", networkKubeSpanExample())
return doc
@ -1135,47 +1102,6 @@ func (InstallDiskSelector) Doc() *encoder.Doc {
return doc
}
func (TimeConfig) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "TimeConfig",
Comments: [3]string{"" /* encoder.HeadComment */, "TimeConfig represents the options for configuring time on a machine." /* encoder.LineComment */, "" /* encoder.FootComment */},
Description: "TimeConfig represents the options for configuring time on a machine.",
AppearsIn: []encoder.Appearance{
{
TypeName: "MachineConfig",
FieldName: "time",
},
},
Fields: []encoder.Doc{
{
Name: "disabled",
Type: "bool",
Note: "",
Description: "Indicates if the time service is disabled for the machine.\nDefaults to `false`.",
Comments: [3]string{"" /* encoder.HeadComment */, "Indicates if the time service is disabled for the machine." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "servers",
Type: "[]string",
Note: "",
Description: "description: |\n Specifies time (NTP) servers to use for setting the system time.\n Defaults to `time.cloudflare.com`.\n\n Talos can also sync to the PTP time source (e.g provided by the hypervisor),\n provide the path to the PTP device as \"/dev/ptp0\" or \"/dev/ptp_kvm\".\n",
Comments: [3]string{"" /* encoder.HeadComment */, "description: |" /* encoder.LineComment */, "" /* encoder.FootComment */},
},
{
Name: "bootTimeout",
Type: "Duration",
Note: "",
Description: "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)",
Comments: [3]string{"" /* encoder.HeadComment */, "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
},
}
doc.AddExample("Example configuration for cloudflare ntp server.", machineTimeExample())
return doc
}
func (CoreDNS) Doc() *encoder.Doc {
doc := &encoder.Doc{
Type: "CoreDNS",
@ -2652,7 +2578,6 @@ func GetFileDoc() *encoder.FileDoc {
NetworkConfig{}.Doc(),
InstallConfig{}.Doc(),
InstallDiskSelector{}.Doc(),
TimeConfig{}.Doc(),
CoreDNS{}.Doc(),
Endpoint{}.Doc(),
ControlPlaneConfig{}.Doc(),

View File

@ -0,0 +1,83 @@
---
description: ResolverConfig is a config document to configure DNS resolving.
title: ResolverConfig
---
<!-- markdownlint-disable -->
{{< highlight yaml >}}
apiVersion: v1alpha1
kind: ResolverConfig
# A list of nameservers (DNS servers) to use for resolving domain names.
nameservers:
- address: 1.1.1.1 # The IP address of the nameserver.
- address: ff08::1 # The IP address of the nameserver.
# Configuration for search domains (in /etc/resolv.conf).
searchDomains:
# A list of search domains to be used for DNS resolution.
domains:
- example.com
{{< /highlight >}}
{{< highlight yaml >}}
apiVersion: v1alpha1
kind: ResolverConfig
# Configuration for search domains (in /etc/resolv.conf).
searchDomains:
disableDefault: true # Disable default search domain configuration from hostname FQDN.
{{< /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. | |
## nameservers[] {#ResolverConfig.nameservers.}
NameserverConfig represents a single nameserver configuration.
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`address` |Addr |The IP address of the nameserver. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
address: 10.0.0.1
{{< /highlight >}}</details> | |
## searchDomains {#ResolverConfig.searchDomains}
SearchDomainsConfig represents search domains configuration.
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`domains` |[]string |A list of search domains to be used for DNS resolution.<br><br>Search domains are appended to unqualified domain names during DNS resolution.<br>For example, if "example.com" is a search domain and a user tries to resolve<br>"host", the system will attempt to resolve "host.example.com".<br><br>This overrides any search domains obtained via DHCP or platform configuration.<br>The default configuration derives the search domain from the hostname FQDN. | |
|`disableDefault` |bool |Disable default search domain configuration from hostname FQDN.<br><br>When set to true, the system will not derive search domains from the hostname FQDN.<br>This allows for a custom configuration of search domains without any defaults. | |

View File

@ -0,0 +1,80 @@
---
description: TimeSyncConfig is a config document to configure time synchronization (NTP).
title: TimeSyncConfig
---
<!-- markdownlint-disable -->
{{< highlight yaml >}}
apiVersion: v1alpha1
kind: TimeSyncConfig
# Specifies NTP configuration to sync the time over network.
ntp:
# Specifies time (NTP) servers to use for setting the system time.
servers:
- pool.ntp.org
{{< /highlight >}}
{{< highlight yaml >}}
apiVersion: v1alpha1
kind: TimeSyncConfig
# Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.
ptp:
# description: |
devices:
- /dev/ptp0
{{< /highlight >}}
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`enabled` |bool |Indicates if the time synchronization is enabled for the machine.<br>Defaults to `true`. | |
|`bootTimeout` |Duration |Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.<br>NTP sync will be still running in the background.<br>Defaults to "infinity" (waiting forever for time sync) | |
|`ntp` |<a href="#TimeSyncConfig.ntp">NTPConfig</a> |Specifies NTP configuration to sync the time over network.<br>Mutually exclusive with PTP configuration. | |
|`ptp` |<a href="#TimeSyncConfig.ptp">PTPConfig</a> |Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.<br>Mutually exclusive with NTP configuration. | |
## ntp {#TimeSyncConfig.ntp}
NTPConfig represents a NTP server configuration.
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`servers` |[]string |Specifies time (NTP) servers to use for setting the system time.<br>Defaults to `time.cloudflare.com`. | |
## ptp {#TimeSyncConfig.ptp}
PTPConfig represents a PTP (Precision Time Protocol) configuration.
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`devices` |[]string |description: |<br> A list of PTP devices to sync with (e.g. provided by the hypervisor).<br><br> A PTP device is typically represented as a character device file in the /dev directory,<br> such as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time<br> with an external time source that supports the Precision Time Protocol.<br> | |

View File

@ -144,11 +144,9 @@ pods:
{{< /highlight >}}</details> | |
|`network` |<a href="#Config.machine.network">NetworkConfig</a> |Provides machine specific network configuration options. <details><summary>Show example(s)</summary>Network definition example.:{{< highlight yaml >}}
network:
# Used to statically set the nameservers for the machine.
nameservers:
- 9.8.7.6
- 8.7.6.5
# Used to statically set arbitrary search domains.
searchDomains:
- example.org
- example.com
@ -190,14 +188,6 @@ env:
env:
https_proxy: http://DOMAIN\USERNAME:PASSWORD@SERVER:PORT/
{{< /highlight >}}</details> |``GRPC_GO_LOG_VERBOSITY_LEVEL``<br />``GRPC_GO_LOG_SEVERITY_LEVEL``<br />``http_proxy``<br />``https_proxy``<br />``no_proxy``<br /> |
|`time` |<a href="#Config.machine.time">TimeConfig</a> |Used to configure the machine's time settings. <details><summary>Show example(s)</summary>Example configuration for cloudflare ntp server.:{{< highlight yaml >}}
time:
disabled: false # Indicates if the time service is disabled for the machine.
# description: |
servers:
- time.cloudflare.com
bootTimeout: 2m0s # Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.
{{< /highlight >}}</details> | |
|`sysctls` |map[string]string |Used to configure the machine's sysctls. <details><summary>Show example(s)</summary>MachineSysctls usage example.:{{< highlight yaml >}}
sysctls:
kernel.domainname: talos.dev
@ -560,11 +550,9 @@ NetworkConfig represents the machine's networking config values.
{{< highlight yaml >}}
machine:
network:
# Used to statically set the nameservers for the machine.
nameservers:
- 9.8.7.6
- 8.7.6.5
# Used to statically set arbitrary search domains.
searchDomains:
- example.org
- example.com
@ -577,21 +565,10 @@ machine:
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`nameservers` |[]string |Used to statically set the nameservers for the machine.<br>Defaults to `1.1.1.1` and `8.8.8.8` <details><summary>Show example(s)</summary>{{< highlight yaml >}}
nameservers:
- 8.8.8.8
- 1.1.1.1
{{< /highlight >}}</details> | |
|`searchDomains` |[]string |Used to statically set arbitrary search domains. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
searchDomains:
- example.org
- example.com
{{< /highlight >}}</details> | |
|`kubespan` |<a href="#Config.machine.network.kubespan">NetworkKubeSpan</a> |Configures KubeSpan feature. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
kubespan:
enabled: true # Enable the KubeSpan feature.
{{< /highlight >}}</details> | |
|`disableSearchDomain` |bool |Disable generating a default search domain in /etc/resolv.conf<br>based on the machine hostname.<br>Defaults to `false`. |`true`<br />`yes`<br />`false`<br />`no`<br /> |
@ -771,34 +748,6 @@ machine:
### time {#Config.machine.time}
TimeConfig represents the options for configuring time on a machine.
{{< highlight yaml >}}
machine:
time:
disabled: false # Indicates if the time service is disabled for the machine.
# description: |
servers:
- time.cloudflare.com
bootTimeout: 2m0s # Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.
{{< /highlight >}}
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`disabled` |bool |Indicates if the time service is disabled for the machine.<br>Defaults to `false`. | |
|`servers` |[]string |description: |<br> Specifies time (NTP) servers to use for setting the system time.<br> Defaults to `time.cloudflare.com`.<br><br> Talos can also sync to the PTP time source (e.g provided by the hypervisor),<br> provide the path to the PTP device as "/dev/ptp0" or "/dev/ptp_kvm".<br> | |
|`bootTimeout` |Duration |Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.<br>NTP sync will be still running in the background.<br>Defaults to "infinity" (waiting forever for time sync) | |
### features {#Config.machine.features}
FeaturesConfig describes individual Talos features that can be switched on or off.

View File

@ -2249,6 +2249,101 @@
"type": "object",
"description": "LinkSelector selects a link to alias."
},
"network.NTPConfig": {
"properties": {
"servers": {
"items": {
"type": "string"
},
"type": "array",
"title": "servers",
"description": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to time.cloudflare.com.\n",
"markdownDescription": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to `time.cloudflare.com`.",
"x-intellij-html-description": "\u003cp\u003eSpecifies time (NTP) servers to use for setting the system time.\nDefaults to \u003ccode\u003etime.cloudflare.com\u003c/code\u003e.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"description": "NTPConfig represents a NTP server configuration."
},
"network.NameserverConfig": {
"properties": {
"address": {
"type": "string",
"pattern": "^[0-9a-f.:]+$",
"title": "address",
"description": "The IP address of the nameserver.\n",
"markdownDescription": "The IP address of the nameserver.",
"x-intellij-html-description": "\u003cp\u003eThe IP address of the nameserver.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"description": "NameserverConfig represents a single nameserver configuration."
},
"network.PTPConfig": {
"properties": {
"devices": {
"items": {
"type": "string"
},
"type": "array",
"title": "devices",
"description": "description: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\n\nA PTP device is typically represented as a character device file in the /dev directory,\n\n\nsuch as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.\n",
"markdownDescription": "description: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\n\n A PTP device is typically represented as a character device file in the /dev directory,\n such as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.",
"x-intellij-html-description": "\u003cp\u003edescription: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\u003c/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eA PTP device is typically represented as a character device file in the /dev directory,\n\u003c/code\u003e\u003c/pre\u003e\n\n\u003cp\u003esuch as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"description": "PTPConfig represents a PTP (Precision Time Protocol) configuration."
},
"network.ResolverConfigV1Alpha1": {
"properties": {
"apiVersion": {
"enum": [
"v1alpha1"
],
"title": "apiVersion",
"description": "apiVersion is the API version of the resource.\n",
"markdownDescription": "apiVersion is the API version of the resource.",
"x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n"
},
"kind": {
"enum": [
"ResolverConfig"
],
"title": "kind",
"description": "kind is the kind of the resource.\n",
"markdownDescription": "kind is the kind of the resource.",
"x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n"
},
"nameservers": {
"items": {
"$ref": "#/$defs/network.NameserverConfig"
},
"type": "array",
"title": "nameservers",
"description": "A list of nameservers (DNS servers) to use for resolving domain names.\n\nNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\n\nThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.\n",
"markdownDescription": "A list of nameservers (DNS servers) to use for resolving domain names.\n\nNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\n\nThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.",
"x-intellij-html-description": "\u003cp\u003eA list of nameservers (DNS servers) to use for resolving domain names.\u003c/p\u003e\n\n\u003cp\u003eNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\u003c/p\u003e\n\n\u003cp\u003eThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.\u003c/p\u003e\n"
},
"searchDomains": {
"$ref": "#/$defs/network.SearchDomainsConfig",
"title": "searchDomains",
"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"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"apiVersion",
"kind"
],
"description": "ResolverConfig is a config document to configure DNS resolving."
},
"network.RouteConfig": {
"properties": {
"destination": {
@ -2391,6 +2486,30 @@
"type": "object",
"description": "RulePortSelector is a port selector for the network rule."
},
"network.SearchDomainsConfig": {
"properties": {
"domains": {
"items": {
"type": "string"
},
"type": "array",
"title": "domains",
"description": "A list of search domains to be used for DNS resolution.\n\nSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if “example.com” is a search domain and a user tries to resolve\n“host”, the system will attempt to resolve “host.example.com”.\n\nThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.\n",
"markdownDescription": "A list of search domains to be used for DNS resolution.\n\nSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if \"example.com\" is a search domain and a user tries to resolve\n\"host\", the system will attempt to resolve \"host.example.com\".\n\nThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.",
"x-intellij-html-description": "\u003cp\u003eA list of search domains to be used for DNS resolution.\u003c/p\u003e\n\n\u003cp\u003eSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if \u0026ldquo;example.com\u0026rdquo; is a search domain and a user tries to resolve\n\u0026ldquo;host\u0026rdquo;, the system will attempt to resolve \u0026ldquo;host.example.com\u0026rdquo;.\u003c/p\u003e\n\n\u003cp\u003eThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.\u003c/p\u003e\n"
},
"disableDefault": {
"type": "boolean",
"title": "disableDefault",
"description": "Disable default search domain configuration from hostname FQDN.\n\nWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.\n",
"markdownDescription": "Disable default search domain configuration from hostname FQDN.\n\nWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.",
"x-intellij-html-description": "\u003cp\u003eDisable default search domain configuration from hostname FQDN.\u003c/p\u003e\n\n\u003cp\u003eWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"description": "SearchDomainsConfig represents search domains configuration."
},
"network.StaticHostConfigV1Alpha1": {
"properties": {
"apiVersion": {
@ -2438,6 +2557,64 @@
],
"description": "StaticHostConfig is a config document to set /etc/hosts entries."
},
"network.TimeSyncConfigV1Alpha1": {
"properties": {
"apiVersion": {
"enum": [
"v1alpha1"
],
"title": "apiVersion",
"description": "apiVersion is the API version of the resource.\n",
"markdownDescription": "apiVersion is the API version of the resource.",
"x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n"
},
"kind": {
"enum": [
"TimeSyncConfig"
],
"title": "kind",
"description": "kind is the kind of the resource.\n",
"markdownDescription": "kind is the kind of the resource.",
"x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n"
},
"enabled": {
"type": "boolean",
"title": "enabled",
"description": "Indicates if the time synchronization is enabled for the machine.\nDefaults to true.\n",
"markdownDescription": "Indicates if the time synchronization is enabled for the machine.\nDefaults to `true`.",
"x-intellij-html-description": "\u003cp\u003eIndicates if the time synchronization is enabled for the machine.\nDefaults to \u003ccode\u003etrue\u003c/code\u003e.\u003c/p\u003e\n"
},
"bootTimeout": {
"type": "string",
"pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$",
"title": "bootTimeout",
"description": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to “infinity” (waiting forever for time sync)\n",
"markdownDescription": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)",
"x-intellij-html-description": "\u003cp\u003eSpecifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \u0026ldquo;infinity\u0026rdquo; (waiting forever for time sync)\u003c/p\u003e\n"
},
"ntp": {
"$ref": "#/$defs/network.NTPConfig",
"title": "ntp",
"description": "Specifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.\n",
"markdownDescription": "Specifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.",
"x-intellij-html-description": "\u003cp\u003eSpecifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.\u003c/p\u003e\n"
},
"ptp": {
"$ref": "#/$defs/network.PTPConfig",
"title": "ptp",
"description": "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.\n",
"markdownDescription": "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.",
"x-intellij-html-description": "\u003cp\u003eSpecific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"required": [
"apiVersion",
"kind"
],
"description": "TimeSyncConfig is a config document to configure time synchronization (NTP)."
},
"network.VLANConfigV1Alpha1": {
"properties": {
"apiVersion": {
@ -4412,13 +4589,6 @@
"markdownDescription": "The `env` field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.",
"x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\u003c/p\u003e\n"
},
"time": {
"$ref": "#/$defs/v1alpha1.TimeConfig",
"title": "time",
"description": "Used to configure the machines time settings.\n",
"markdownDescription": "Used to configure the machine's time settings.",
"x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s time settings.\u003c/p\u003e\n"
},
"sysctls": {
"patternProperties": {
".*": {
@ -4640,39 +4810,12 @@
},
"v1alpha1.NetworkConfig": {
"properties": {
"nameservers": {
"items": {
"type": "string"
},
"type": "array",
"title": "nameservers",
"description": "Used to statically set the nameservers for the machine.\nDefaults to 1.1.1.1 and 8.8.8.8\n",
"markdownDescription": "Used to statically set the nameservers for the machine.\nDefaults to `1.1.1.1` and `8.8.8.8`",
"x-intellij-html-description": "\u003cp\u003eUsed to statically set the nameservers for the machine.\nDefaults to \u003ccode\u003e1.1.1.1\u003c/code\u003e and \u003ccode\u003e8.8.8.8\u003c/code\u003e\u003c/p\u003e\n"
},
"searchDomains": {
"items": {
"type": "string"
},
"type": "array",
"title": "searchDomains",
"description": "Used to statically set arbitrary search domains.\n",
"markdownDescription": "Used to statically set arbitrary search domains.",
"x-intellij-html-description": "\u003cp\u003eUsed to statically set arbitrary search domains.\u003c/p\u003e\n"
},
"kubespan": {
"$ref": "#/$defs/v1alpha1.NetworkKubeSpan",
"title": "kubespan",
"description": "Configures KubeSpan feature.\n",
"markdownDescription": "Configures KubeSpan feature.",
"x-intellij-html-description": "\u003cp\u003eConfigures KubeSpan feature.\u003c/p\u003e\n"
},
"disableSearchDomain": {
"type": "boolean",
"title": "disableSearchDomain",
"description": "Disable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to false.\n",
"markdownDescription": "Disable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to `false`.",
"x-intellij-html-description": "\u003cp\u003eDisable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
@ -4886,38 +5029,6 @@
"type": "object",
"description": "SchedulerConfig represents the kube scheduler configuration options."
},
"v1alpha1.TimeConfig": {
"properties": {
"disabled": {
"type": "boolean",
"title": "disabled",
"description": "Indicates if the time service is disabled for the machine.\nDefaults to false.\n",
"markdownDescription": "Indicates if the time service is disabled for the machine.\nDefaults to `false`.",
"x-intellij-html-description": "\u003cp\u003eIndicates if the time service is disabled for the machine.\nDefaults to \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n"
},
"servers": {
"items": {
"type": "string"
},
"type": "array",
"title": "servers",
"description": "description: |\n Specifies time (NTP) servers to use for setting the system time.\n Defaults to time.cloudflare.com.\n\nTalos can also sync to the PTP time source (e.g provided by the hypervisor),\n provide the path to the PTP device as “/dev/ptp0” or “/dev/ptp_kvm”.\n",
"markdownDescription": "description: |\n Specifies time (NTP) servers to use for setting the system time.\n Defaults to `time.cloudflare.com`.\n\n Talos can also sync to the PTP time source (e.g provided by the hypervisor),\n provide the path to the PTP device as \"/dev/ptp0\" or \"/dev/ptp_kvm\".",
"x-intellij-html-description": "\u003cp\u003edescription: |\n Specifies time (NTP) servers to use for setting the system time.\n Defaults to \u003ccode\u003etime.cloudflare.com\u003c/code\u003e.\u003c/p\u003e\n\n\u003cp\u003eTalos can also sync to the PTP time source (e.g provided by the hypervisor),\n provide the path to the PTP device as \u0026ldquo;/dev/ptp0\u0026rdquo; or \u0026ldquo;/dev/ptp_kvm\u0026rdquo;.\u003c/p\u003e\n"
},
"bootTimeout": {
"type": "string",
"pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$",
"title": "bootTimeout",
"description": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to “infinity” (waiting forever for time sync)\n",
"markdownDescription": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)",
"x-intellij-html-description": "\u003cp\u003eSpecifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \u0026ldquo;infinity\u0026rdquo; (waiting forever for time sync)\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object",
"description": "TimeConfig represents the options for configuring time on a machine."
},
"v1alpha1.UdevConfig": {
"properties": {
"rules": {
@ -5037,12 +5148,18 @@
{
"$ref": "#/$defs/network.LinkAliasConfigV1Alpha1"
},
{
"$ref": "#/$defs/network.ResolverConfigV1Alpha1"
},
{
"$ref": "#/$defs/network.RuleConfigV1Alpha1"
},
{
"$ref": "#/$defs/network.StaticHostConfigV1Alpha1"
},
{
"$ref": "#/$defs/network.TimeSyncConfigV1Alpha1"
},
{
"$ref": "#/$defs/network.VLANConfigV1Alpha1"
},