Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: prefer longer prefixes in IPv6 only environments as default Nod… #9749

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion internal/app/machined/pkg/controllers/network/node_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ import (
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/gen/optional"
"github.com/siderolabs/gen/value"
"go.uber.org/zap"

talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config"
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
"github.com/siderolabs/talos/pkg/machinery/resources/config"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
)

Expand All @@ -32,6 +36,12 @@ func (ctrl *NodeAddressController) Name() string {
// Inputs implements controller.Controller interface.
func (ctrl *NodeAddressController) Inputs() []controller.Input {
return []controller.Input{
{
Namespace: config.NamespaceName,
Type: config.MachineConfigType,
ID: optional.Some(config.V1Alpha1ID),
Kind: controller.InputWeak,
},
{
Namespace: network.NamespaceName,
Type: network.AddressStatusType,
Expand Down Expand Up @@ -63,7 +73,7 @@ func (ctrl *NodeAddressController) Outputs() []controller.Output {
// Run implements controller.Controller interface.
//
//nolint:gocyclo,cyclop
func (ctrl *NodeAddressController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error {
func (ctrl *NodeAddressController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
var addressStatusController AddressStatusController

addressStatusControllerName := addressStatusController.Name()
Expand All @@ -75,6 +85,17 @@ func (ctrl *NodeAddressController) Run(ctx context.Context, r controller.Runtime
case <-r.EventCh():
}

var cfgProvider talosconfig.Config

cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.V1Alpha1ID)
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting config: %w", err)
}
} else if cfg.Config().Machine() != nil {
cfgProvider = cfg.Config()
}

// fetch link and address status resources
links, err := r.List(ctx, resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined))
if err != nil {
Expand Down Expand Up @@ -158,6 +179,15 @@ func (ctrl *NodeAddressController) Run(ctx context.Context, r controller.Runtime
current = deduplicateIPPrefixes(current)
routed = deduplicateIPPrefixes(routed)

// if preference for long prefixes is enabled, we re-sort
if cfgProvider != nil && cfgProvider.Machine().Features().LongPrefixPreferenceEnabled() {
logger.Debug("long prefix preference enabled")
slices.SortFunc(current, compareContainPrefix)
slices.SortFunc(routed, compareContainPrefix)
// the address with highest preference should be the first address
defaultAddress = current[0]
}

touchedIDs := make(map[resource.ID]struct{})

// update output resources
Expand Down Expand Up @@ -339,3 +369,15 @@ func updateAccumulativeAddresses(ctx context.Context, r controller.Runtime, id r

return nil
}

func compareContainPrefix(a, b netip.Prefix) int {
// the list is already sorted alphabetically, so we only need
// to test if a or b contains the other
if a.Contains(b.Addr()) {
return 1
} else if b.Contains(a.Addr()) {
return -1
}

return 0
}
96 changes: 96 additions & 0 deletions internal/app/machined/pkg/controllers/network/node_address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ import (
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/resource/rtestutils"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"

"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network"
"github.com/siderolabs/talos/pkg/machinery/config/container"
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
"github.com/siderolabs/talos/pkg/machinery/resources/config"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime"
)
Expand Down Expand Up @@ -278,6 +282,98 @@ func (suite *NodeAddressSuite) TestFilterOverlappingSubnets() {
)
}

func (suite *NodeAddressSuite) TestLongPrefixPreference() {
var addressStatusController netctrl.AddressStatusController

cfg := config.NewMachineConfig(
container.NewV1Alpha1(
&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{
MachineFeatures: &v1alpha1.FeaturesConfig{
LongPrefixPreference: pointer.To(true),
},
},
},
),
)

suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg))

linkEth0 := network.NewLinkStatus(network.NamespaceName, "eth0")
linkEth0.TypedSpec().Type = nethelpers.LinkEther
linkEth0.TypedSpec().LinkState = true
linkEth0.TypedSpec().Index = 1
suite.Require().NoError(suite.State().Create(suite.Ctx(), linkEth0))

linkLo := network.NewLinkStatus(network.NamespaceName, "lo")
linkLo.TypedSpec().Type = nethelpers.LinkLoopbck
linkLo.TypedSpec().LinkState = true
linkLo.TypedSpec().Index = 2
suite.Require().NoError(suite.State().Create(suite.Ctx(), linkLo))

newAddress := func(addr netip.Prefix, scope nethelpers.Scope, flags nethelpers.AddressFlags, link *network.LinkStatus) {
addressStatus := network.NewAddressStatus(network.NamespaceName, network.AddressID(link.Metadata().ID(), addr))
addressStatus.TypedSpec().Address = addr
addressStatus.TypedSpec().LinkName = link.Metadata().ID()
addressStatus.TypedSpec().LinkIndex = link.TypedSpec().Index
addressStatus.TypedSpec().Scope = scope
addressStatus.TypedSpec().Flags = flags
suite.Require().NoError(
suite.State().Create(
suite.Ctx(),
addressStatus,
state.WithCreateOwner(addressStatusController.Name()),
),
)
}

for _, a := range []struct {
Addr string
Scope nethelpers.Scope
Flags nethelpers.AddressFlags
Link *network.LinkStatus
}{
{"fd01:cafe::5054:ff:fe1f:c7bd/64", nethelpers.ScopeGlobal, nethelpers.AddressFlags(nethelpers.AddressManagementTemp), linkEth0},
{"fd01:cafe::f14c:9fa1:8496:557f/128", nethelpers.ScopeGlobal, nethelpers.AddressFlags(nethelpers.AddressPermanent), linkEth0},
{"fe80::5054:ff:fe1f:c7bd/64", nethelpers.ScopeLink, nethelpers.AddressFlags(nethelpers.AddressPermanent), linkEth0},
{"127.0.0.1/8", nethelpers.ScopeHost, nethelpers.AddressFlags(nethelpers.AddressPermanent), linkLo},
{"169.254.116.108/32", nethelpers.ScopeHost, nethelpers.AddressFlags(nethelpers.AddressPermanent), linkLo},
{"::1/128", nethelpers.ScopeHost, nethelpers.AddressFlags(nethelpers.AddressPermanent), linkLo},
} {
newAddress(netip.MustParsePrefix(a.Addr), a.Scope, a.Flags, a.Link)
}

rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(),
[]resource.ID{
network.NodeAddressDefaultID,
network.NodeAddressCurrentID,
network.NodeAddressRoutedID,
},
func(r *network.NodeAddress, asrt *assert.Assertions) {
addrs := r.TypedSpec().Addresses

switch r.Metadata().ID() {
case network.NodeAddressDefaultID:
asrt.Equal(
ipList("fd01:cafe::f14c:9fa1:8496:557f/128"),
addrs,
)
case network.NodeAddressRoutedID:
asrt.Equal(
ipList("fd01:cafe::f14c:9fa1:8496:557f/128 fd01:cafe::5054:ff:fe1f:c7bd/64"),
addrs,
)
case network.NodeAddressCurrentID:
asrt.Equal(
ipList("fd01:cafe::f14c:9fa1:8496:557f/128 fd01:cafe::5054:ff:fe1f:c7bd/64"),
addrs,
)
}
},
)
}

//nolint:gocyclo
func (suite *NodeAddressSuite) TestDefaultAddressChange() {
var addressStatusController netctrl.AddressStatusController
Expand Down
1 change: 1 addition & 0 deletions pkg/machinery/config/config/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ type Features interface {
DiskQuotaSupportEnabled() bool
HostDNS() HostDNS
KubePrism() KubePrism
LongPrefixPreferenceEnabled() bool
ImageCacheEnabled() bool
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/machinery/config/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,8 @@ func (contract *VersionContract) AddExcludeFromExternalLoadBalancer() bool {
func (contract *VersionContract) SecureBootEnrollEnforcementSupported() bool {
return contract.Greater(TalosVersion1_7)
}

// LongPrefixPreferenceEnabled returns true starting with Talos 1.8 to default to new address sorting scheme.
func (contract *VersionContract) LongPrefixPreferenceEnabled() bool {
return contract.Greater(TalosVersion1_8)
}
11 changes: 11 additions & 0 deletions pkg/machinery/config/contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func TestContractCurrent(t *testing.T) {
assert.True(t, contract.HostDNSForwardKubeDNSToHost())
assert.True(t, contract.AddExcludeFromExternalLoadBalancer())
assert.True(t, contract.SecureBootEnrollEnforcementSupported())
assert.True(t, contract.LongPrefixPreferenceEnabled())
}

func TestContract1_9(t *testing.T) {
Expand All @@ -88,6 +89,7 @@ func TestContract1_9(t *testing.T) {
assert.True(t, contract.HostDNSForwardKubeDNSToHost())
assert.True(t, contract.AddExcludeFromExternalLoadBalancer())
assert.True(t, contract.SecureBootEnrollEnforcementSupported())
assert.True(t, contract.LongPrefixPreferenceEnabled())
}

func TestContract1_8(t *testing.T) {
Expand All @@ -111,6 +113,7 @@ func TestContract1_8(t *testing.T) {
assert.True(t, contract.HostDNSForwardKubeDNSToHost())
assert.True(t, contract.AddExcludeFromExternalLoadBalancer())
assert.True(t, contract.SecureBootEnrollEnforcementSupported())
assert.False(t, contract.LongPrefixPreferenceEnabled())
}

func TestContract1_7(t *testing.T) {
Expand All @@ -134,6 +137,7 @@ func TestContract1_7(t *testing.T) {
assert.False(t, contract.HostDNSForwardKubeDNSToHost())
assert.False(t, contract.AddExcludeFromExternalLoadBalancer())
assert.False(t, contract.SecureBootEnrollEnforcementSupported())
assert.False(t, contract.LongPrefixPreferenceEnabled())
}

func TestContract1_6(t *testing.T) {
Expand All @@ -157,6 +161,7 @@ func TestContract1_6(t *testing.T) {
assert.False(t, contract.HostDNSForwardKubeDNSToHost())
assert.False(t, contract.AddExcludeFromExternalLoadBalancer())
assert.False(t, contract.SecureBootEnrollEnforcementSupported())
assert.False(t, contract.LongPrefixPreferenceEnabled())
}

func TestContract1_5(t *testing.T) {
Expand All @@ -180,6 +185,7 @@ func TestContract1_5(t *testing.T) {
assert.False(t, contract.HostDNSForwardKubeDNSToHost())
assert.False(t, contract.AddExcludeFromExternalLoadBalancer())
assert.False(t, contract.SecureBootEnrollEnforcementSupported())
assert.False(t, contract.LongPrefixPreferenceEnabled())
}

func TestContract1_4(t *testing.T) {
Expand All @@ -203,6 +209,7 @@ func TestContract1_4(t *testing.T) {
assert.False(t, contract.HostDNSForwardKubeDNSToHost())
assert.False(t, contract.AddExcludeFromExternalLoadBalancer())
assert.False(t, contract.SecureBootEnrollEnforcementSupported())
assert.False(t, contract.LongPrefixPreferenceEnabled())
}

func TestContract1_3(t *testing.T) {
Expand All @@ -226,6 +233,7 @@ func TestContract1_3(t *testing.T) {
assert.False(t, contract.HostDNSForwardKubeDNSToHost())
assert.False(t, contract.AddExcludeFromExternalLoadBalancer())
assert.False(t, contract.SecureBootEnrollEnforcementSupported())
assert.False(t, contract.LongPrefixPreferenceEnabled())
}

func TestContract1_2(t *testing.T) {
Expand All @@ -249,6 +257,7 @@ func TestContract1_2(t *testing.T) {
assert.False(t, contract.HostDNSForwardKubeDNSToHost())
assert.False(t, contract.AddExcludeFromExternalLoadBalancer())
assert.False(t, contract.SecureBootEnrollEnforcementSupported())
assert.False(t, contract.LongPrefixPreferenceEnabled())
}

func TestContract1_1(t *testing.T) {
Expand All @@ -272,6 +281,7 @@ func TestContract1_1(t *testing.T) {
assert.False(t, contract.HostDNSForwardKubeDNSToHost())
assert.False(t, contract.AddExcludeFromExternalLoadBalancer())
assert.False(t, contract.SecureBootEnrollEnforcementSupported())
assert.False(t, contract.LongPrefixPreferenceEnabled())
}

func TestContract1_0(t *testing.T) {
Expand All @@ -295,4 +305,5 @@ func TestContract1_0(t *testing.T) {
assert.False(t, contract.HostDNSForwardKubeDNSToHost())
assert.False(t, contract.AddExcludeFromExternalLoadBalancer())
assert.False(t, contract.SecureBootEnrollEnforcementSupported())
assert.False(t, contract.LongPrefixPreferenceEnabled())
}
4 changes: 4 additions & 0 deletions pkg/machinery/config/generate/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ func (in *Input) init() ([]config.Document, error) {
machine.MachineFeatures.DiskQuotaSupport = pointer.To(true)
}

if in.Options.VersionContract.LongPrefixPreferenceEnabled() {
machine.MachineFeatures.LongPrefixPreference = pointer.To(true)
}

if kubePrismPort, optionSet := in.Options.KubePrismPort.Get(); optionSet { // default to enabled, but if set explicitly, allow it to be disabled
if kubePrismPort > 0 {
machine.MachineFeatures.KubePrismSupport = &v1alpha1.KubePrism{
Expand Down
6 changes: 5 additions & 1 deletion pkg/machinery/config/generate/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/siderolabs/talos/pkg/machinery/constants"
)

//nolint:gocyclo
//nolint:gocyclo,cyclop
func (in *Input) worker() ([]config.Document, error) {
v1alpha1Config := &v1alpha1.Config{
ConfigVersion: "v1alpha1",
Expand Down Expand Up @@ -72,6 +72,10 @@ func (in *Input) worker() ([]config.Document, error) {
machine.MachineFeatures.DiskQuotaSupport = pointer.To(true)
}

if in.Options.VersionContract.LongPrefixPreferenceEnabled() {
machine.MachineFeatures.LongPrefixPreference = pointer.To(true)
}

if kubePrismPort, optionSet := in.Options.KubePrismPort.Get(); optionSet { // default to enabled, but if set explicitly, allow it to be disabled
if kubePrismPort > 0 {
machine.MachineFeatures.KubePrismSupport = &v1alpha1.KubePrism{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ machine:
features:
rbac: true
stableHostname: true
longPrefixPreference: true
apidCheckExtKeyUsage: true
diskQuotaSupport: true
kubePrism:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ machine:
features:
rbac: true
stableHostname: true
longPrefixPreference: true
apidCheckExtKeyUsage: true
diskQuotaSupport: true
kubePrism:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ machine:
features:
rbac: true
stableHostname: true
longPrefixPreference: true
apidCheckExtKeyUsage: true
diskQuotaSupport: true
kubePrism:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ machine:
features:
rbac: true
stableHostname: true
longPrefixPreference: true
apidCheckExtKeyUsage: true
diskQuotaSupport: true
kubePrism:
Expand Down
4 changes: 4 additions & 0 deletions pkg/machinery/config/types/v1alpha1/v1alpha1_features.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ func (f *FeaturesConfig) KubePrism() config.KubePrism {
return f.KubePrismSupport
}

// LongPrefixPreferenceEnabled implements config.Features interface.
func (f *FeaturesConfig) LongPrefixPreferenceEnabled() bool {
return pointer.SafeDeref(f.LongPrefixPreference)

// ImageCacheEnabled implements config.Features interface.
func (f *FeaturesConfig) ImageCacheEnabled() bool {
return pointer.SafeDeref(f.ImageCache)
Expand Down
3 changes: 3 additions & 0 deletions pkg/machinery/config/types/v1alpha1/v1alpha1_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2147,6 +2147,9 @@ type FeaturesConfig struct {
// Enable stable default hostname.
StableHostname *bool `yaml:"stableHostname,omitempty"`
// description: |
// Prefers longer prefixes and will sort addresses with longer prefixes first.
LongPrefixPreference *bool `yaml:"longPrefixPreference,omitempty"`
// description: |
// Configure Talos API access from Kubernetes pods.
//
// This feature is disabled if the feature config is not specified.
Expand Down