feat: bring unconfigured links with link carrier up by default

This matches previous networkd implementation to make sure we can run
DHCP on the interfaces which are not explicitly brought up.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
This commit is contained in:
Andrey Smirnov 2021-06-08 19:20:21 +03:00 committed by talos-bot
parent 02bd657b25
commit f93c9c8fa6
2 changed files with 154 additions and 8 deletions

View File

@ -42,6 +42,11 @@ func (ctrl *LinkConfigController) Inputs() []controller.Input {
ID: pointer.ToString(config.V1Alpha1ID), ID: pointer.ToString(config.V1Alpha1ID),
Kind: controller.InputWeak, Kind: controller.InputWeak,
}, },
{
Namespace: network.NamespaceName,
Type: network.LinkStatusType,
Kind: controller.InputWeak,
},
} }
} }
@ -111,7 +116,7 @@ func (ctrl *LinkConfigController) Run(ctx context.Context, r controller.Runtime,
} }
// parse kernel cmdline for the interface name // parse kernel cmdline for the interface name
cmdlineLink := ctrl.parseCmdline(logger) cmdlineLink, cmdlineIgnored := ctrl.parseCmdline(logger)
if cmdlineLink.Name != "" { if cmdlineLink.Name != "" {
if _, ignored := ignoredInterfaces[cmdlineLink.Name]; !ignored { if _, ignored := ignoredInterfaces[cmdlineLink.Name]; !ignored {
var ids []string var ids []string
@ -127,7 +132,7 @@ func (ctrl *LinkConfigController) Run(ctx context.Context, r controller.Runtime,
} }
} }
// parse machine configuration for static routes // parse machine configuration for link specs
if cfgProvider != nil { if cfgProvider != nil {
links := ctrl.parseMachineConfiguration(logger, cfgProvider) links := ctrl.parseMachineConfiguration(logger, cfgProvider)
@ -143,8 +148,56 @@ func (ctrl *LinkConfigController) Run(ctx context.Context, r controller.Runtime,
} }
} }
// list link for cleanup // bring up any physical link not mentioned explicitly in the machine configuration
list, err := r.List(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.LinkSpecType, "", resource.VersionUndefined)) configuredLinks := map[string]struct{}{}
for _, linkName := range cmdlineIgnored {
configuredLinks[linkName] = struct{}{}
}
if cmdlineLink.Name != "" {
configuredLinks[cmdlineLink.Name] = struct{}{}
}
if cfgProvider != nil {
for _, device := range cfgProvider.Machine().Network().Devices() {
configuredLinks[device.Interface()] = struct{}{}
}
}
list, err := r.List(ctx, resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined))
if err != nil {
return fmt.Errorf("error listing link statuses: %w", err)
}
for _, item := range list.Items {
linkStatus := item.(*network.LinkStatus) //nolint:errcheck,forcetypeassert
if _, configured := configuredLinks[linkStatus.Metadata().ID()]; !configured {
if linkStatus.Physical() {
var ids []string
ids, err = ctrl.apply(ctx, r, []network.LinkSpecSpec{
{
Name: linkStatus.Metadata().ID(),
Up: true,
ConfigLayer: network.ConfigDefault,
},
})
if err != nil {
return fmt.Errorf("error applying default link up: %w", err)
}
for _, id := range ids {
touchedIDs[id] = struct{}{}
}
}
}
}
// list links for cleanup
list, err = r.List(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.LinkSpecType, "", resource.VersionUndefined))
if err != nil { if err != nil {
return fmt.Errorf("error listing resources: %w", err) return fmt.Errorf("error listing resources: %w", err)
} }
@ -189,23 +242,23 @@ func (ctrl *LinkConfigController) apply(ctx context.Context, r controller.Runtim
return ids, nil return ids, nil
} }
func (ctrl *LinkConfigController) parseCmdline(logger *zap.Logger) network.LinkSpecSpec { func (ctrl *LinkConfigController) parseCmdline(logger *zap.Logger) (network.LinkSpecSpec, []string) {
if ctrl.Cmdline == nil { if ctrl.Cmdline == nil {
return network.LinkSpecSpec{} return network.LinkSpecSpec{}, nil
} }
settings, err := ParseCmdlineNetwork(ctrl.Cmdline) settings, err := ParseCmdlineNetwork(ctrl.Cmdline)
if err != nil { if err != nil {
logger.Info("ignoring error", zap.Error(err)) logger.Info("ignoring error", zap.Error(err))
return network.LinkSpecSpec{} return network.LinkSpecSpec{}, nil
} }
return network.LinkSpecSpec{ return network.LinkSpecSpec{
Name: settings.LinkName, Name: settings.LinkName,
Up: true, Up: true,
ConfigLayer: network.ConfigCmdline, ConfigLayer: network.ConfigCmdline,
} }, settings.IgnoreInterfaces
} }
//nolint:gocyclo //nolint:gocyclo

View File

@ -97,6 +97,28 @@ func (suite *LinkConfigSuite) assertLinks(requiredIDs []string, check func(*netw
return nil return nil
} }
func (suite *LinkConfigSuite) assertNoLinks(unexpectedIDs []string) error {
unexpIDs := make(map[string]struct{}, len(unexpectedIDs))
for _, id := range unexpectedIDs {
unexpIDs[id] = struct{}{}
}
resources, err := suite.state.List(suite.ctx, resource.NewMetadata(network.ConfigNamespaceName, network.LinkSpecType, "", resource.VersionUndefined))
if err != nil {
return err
}
for _, res := range resources.Items {
_, unexpected := unexpIDs[res.Metadata().ID()]
if unexpected {
return retry.ExpectedErrorf("unexpected ID %q", res.Metadata().ID())
}
}
return nil
}
func (suite *LinkConfigSuite) TestLoopback() { func (suite *LinkConfigSuite) TestLoopback() {
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.LinkConfigController{})) suite.Require().NoError(suite.runtime.RegisterController(&netctrl.LinkConfigController{}))
@ -289,6 +311,77 @@ func (suite *LinkConfigSuite) TestMachineConfiguration() {
})) }))
} }
func (suite *LinkConfigSuite) TestDefaultUp() {
suite.Require().NoError(suite.runtime.RegisterController(&netctrl.LinkConfigController{
Cmdline: procfs.NewCmdline("talos.network.interface.ignore=eth2"),
}))
for _, link := range []string{"eth0", "eth1", "eth2"} {
linkStatus := network.NewLinkStatus(network.NamespaceName, link)
linkStatus.TypedSpec().Type = nethelpers.LinkEther
linkStatus.TypedSpec().LinkState = true
suite.Require().NoError(suite.state.Create(suite.ctx, linkStatus))
}
u, err := url.Parse("https://foo:6443")
suite.Require().NoError(err)
cfg := config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{
MachineNetwork: &v1alpha1.NetworkConfig{
NetworkInterfaces: []*v1alpha1.Device{
{
DeviceInterface: "eth0",
DeviceVlans: []*v1alpha1.Vlan{
{
VlanID: 24,
VlanCIDR: "10.0.0.1/8",
},
{
VlanID: 48,
VlanCIDR: "10.0.0.2/8",
},
},
},
},
},
},
ClusterConfig: &v1alpha1.ClusterConfig{
ControlPlane: &v1alpha1.ControlPlaneConfig{
Endpoint: &v1alpha1.Endpoint{
URL: u,
},
},
},
})
suite.Require().NoError(suite.state.Create(suite.ctx, cfg))
suite.startRuntime()
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertLinks([]string{
"default/eth1",
}, func(r *network.LinkSpec) error {
suite.Assert().Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer)
suite.Assert().True(r.TypedSpec().Up)
return nil
})
}))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
return suite.assertNoLinks([]string{
"default/eth0",
"default/eth2",
})
}))
}
func TestLinkConfigSuite(t *testing.T) { func TestLinkConfigSuite(t *testing.T) {
suite.Run(t, new(LinkConfigSuite)) suite.Run(t, new(LinkConfigSuite))
} }