Skip to content

Commit

Permalink
feat: implement new address sorting algorithm
Browse files Browse the repository at this point in the history
Fixes #9725

See #9749

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
  • Loading branch information
smira committed Dec 5, 2024
1 parent 9081506 commit 7d65071
Show file tree
Hide file tree
Showing 40 changed files with 1,911 additions and 820 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2024-12-04T12:34:25Z by kres 232fe63.
# Generated on 2024-12-04T15:25:22Z by kres 232fe63.

name: default
concurrency:
Expand Down Expand Up @@ -2208,6 +2208,14 @@ jobs:
WITH_DISK_ENCRYPTION: "true"
run: |
sudo -E make e2e-qemu
- name: e2e-node-address-v2
env:
GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image
IMAGE_REGISTRY: registry.dev.siderolabs.io
SHORT_INTEGRATION_TEST: "yes"
WITH_CONFIG_PATCH: '@hack/test/patches/node-address-v2.yaml'
run: |
sudo -E make e2e-qemu
- name: save artifacts
if: always()
uses: actions/upload-artifact@v4
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/integration-misc-2-cron.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2024-11-28T13:53:18Z by kres 232fe63.
# Generated on 2024-12-04T15:25:22Z by kres 232fe63.

name: integration-misc-2-cron
concurrency:
Expand Down Expand Up @@ -110,6 +110,14 @@ jobs:
WITH_DISK_ENCRYPTION: "true"
run: |
sudo -E make e2e-qemu
- name: e2e-node-address-v2
env:
GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image
IMAGE_REGISTRY: registry.dev.siderolabs.io
SHORT_INTEGRATION_TEST: "yes"
WITH_CONFIG_PATCH: '@hack/test/patches/node-address-v2.yaml'
run: |
sudo -E make e2e-qemu
- name: save artifacts
if: always()
uses: actions/upload-artifact@v4
Expand Down
8 changes: 8 additions & 0 deletions .kres.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,14 @@ spec:
VIA_MAINTENANCE_MODE: true
WITH_DISK_ENCRYPTION: true
IMAGE_REGISTRY: registry.dev.siderolabs.io
- name: e2e-node-address-v2
command: e2e-qemu
withSudo: true
environment:
GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image
SHORT_INTEGRATION_TEST: yes
WITH_CONFIG_PATCH: "@hack/test/patches/node-address-v2.yaml"
IMAGE_REGISTRY: registry.dev.siderolabs.io
- name: save-talos-logs
conditions:
- always
Expand Down
6 changes: 6 additions & 0 deletions api/resource/definitions/enums/enums.proto
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ enum NethelpersAddressFlag {
ADDRESS_STABLE_PRIVACY = 2048;
}

// NethelpersAddressSortAlgorithm is an internal address sorting algorithm.
enum NethelpersAddressSortAlgorithm {
ADDRESS_SORT_ALGORITHM_V1 = 0;
ADDRESS_SORT_ALGORITHM_V2 = 1;
}

// NethelpersADSelect is ADSelect.
enum NethelpersADSelect {
AD_SELECT_STABLE = 0;
Expand Down
6 changes: 6 additions & 0 deletions api/resource/definitions/network/network.proto
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,15 @@ message NodeAddressFilterSpec {
repeated common.NetIPPrefix exclude_subnets = 2;
}

// NodeAddressSortAlgorithmSpec describes a filter for NodeAddresses.
message NodeAddressSortAlgorithmSpec {
talos.resource.definitions.enums.NethelpersAddressSortAlgorithm algorithm = 1;
}

// NodeAddressSpec describes a set of node addresses.
message NodeAddressSpec {
repeated common.NetIPPrefix addresses = 1;
talos.resource.definitions.enums.NethelpersAddressSortAlgorithm sort_algorithm = 2;
}

// OperatorSpecSpec describes DNS resolvers.
Expand Down
13 changes: 13 additions & 0 deletions hack/release.toml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,19 @@ options ndots:5
Talos now supports providing a local [Image Cache](https://www.talos.dev/v1.9/talos-guides/configuration/image-cache/) for container images.
"""

[notes.node-address-sort]
title = "Node Address Sort"
description = """\
Talos supports new experimental address sort algorithm for `NodeAddress` which are used to pick up default addresses for kubelet, etcd, etc.
It can be enabled with the following config patch:
```yaml
machine:
features:
nodeAddressSortAlgorithm: v2
"""

[make_deps]

[make_deps.tools]
Expand Down
3 changes: 3 additions & 0 deletions hack/test/patches/node-address-v2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
machine:
features:
nodeAddressSortAlgorithm: v2
22 changes: 22 additions & 0 deletions internal/app/machined/pkg/controllers/k8s/nodeip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,28 @@ func (suite *NodeIPSuite) TestReconcileNoMatch() {
})
}

func (suite *NodeIPSuite) TestReconcileIPv6Denies() {
cfg := k8s.NewNodeIPConfig(k8s.NamespaceName, k8s.KubeletID)
cfg.TypedSpec().ValidSubnets = []string{"::/0", "!fd01:cafe::f14c:9fa1:8496:557f/128"}
suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg))

addresses := network.NewNodeAddress(
network.NamespaceName,
network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s),
)

addresses.TypedSpec().Addresses = []netip.Prefix{
netip.MustParsePrefix("fd01:cafe::f14c:9fa1:8496:557f/128"),
netip.MustParsePrefix("fd01:cafe::5054:ff:fe1f:c7bd/64"),
}

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

rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(nodeIP *k8s.NodeIP, asrt *assert.Assertions) {
asrt.Equal("[fd01:cafe::5054:ff:fe1f:c7bd]", fmt.Sprintf("%s", nodeIP.TypedSpec().Addresses))
})
}

func TestNodeIPSuite(t *testing.T) {
t.Parallel()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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 addressutil contains helpers working with addresses.
package addressutil

import "net/netip"

// DeduplicateIPPrefixes removes duplicates from the given list of prefixes.
//
// The input list must be sorted.
// DeduplicateIPPrefixes performs in-place deduplication.
func DeduplicateIPPrefixes(in []netip.Prefix) []netip.Prefix {
// assumes that current is sorted
n := 0

var prev netip.Prefix

for _, x := range in {
if prev != x {
in[n] = x
n++
}

prev = x
}

return in[:n]
}

// FilterIPs filters the given list of IP prefixes based on the given include and exclude subnets.
//
// If includeSubnets is not empty, only IPs that are in one of the subnets are included.
// If excludeSubnets is not empty, IPs that are in one of the subnets are excluded.
func FilterIPs(addrs []netip.Prefix, includeSubnets, excludeSubnets []netip.Prefix) []netip.Prefix {
result := make([]netip.Prefix, 0, len(addrs))

outer:
for _, ip := range addrs {
if len(includeSubnets) > 0 {
matchesAny := false

for _, subnet := range includeSubnets {
if subnet.Contains(ip.Addr()) {
matchesAny = true

break
}
}

if !matchesAny {
continue outer
}
}

for _, subnet := range excludeSubnets {
if subnet.Contains(ip.Addr()) {
continue outer
}
}

result = append(result, ip)
}

return result
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// 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 addressutil_test

import (
"net/netip"
"testing"

"github.com/stretchr/testify/assert"

"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/internal/addressutil"
)

func TestDeduplicateIPPrefixes(t *testing.T) {
t.Parallel()

for _, test := range []struct {
name string
in []netip.Prefix

out []netip.Prefix
}{
{
name: "empty",
},
{
name: "single",
in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("1.2.3.4/32")},

out: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")},
},
{
name: "many",
in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("1.2.3.4/24"), netip.MustParsePrefix("2000::aebc/64"), netip.MustParsePrefix("2000::aebc/64")},

out: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("1.2.3.4/24"), netip.MustParsePrefix("2000::aebc/64")},
},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()

got := addressutil.DeduplicateIPPrefixes(test.in)

assert.Equal(t, test.out, got)
})
}
}

// TestFilterIPs tests the FilterIPs function.
func TestFilterIPs(t *testing.T) {
t.Parallel()

for _, test := range []struct {
name string

in []netip.Prefix
include []netip.Prefix
exclude []netip.Prefix

out []netip.Prefix
}{
{
name: "empty filters",

in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("2000::aebc/64")},

out: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("2000::aebc/64")},
},
{
name: "v4 only",

in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("2000::aebc/64")},
include: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")},

out: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")},
},
{
name: "v6 only",

in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("2000::aebc/64")},
exclude: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")},

out: []netip.Prefix{netip.MustParsePrefix("2000::aebc/64")},
},
{
name: "include and exclude",

in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("3.4.5.6/24"), netip.MustParsePrefix("2000::aebc/64")},
include: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")},
exclude: []netip.Prefix{netip.MustParsePrefix("3.0.0.0/8")},

out: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")},
},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()

got := addressutil.FilterIPs(test.in, test.include, test.exclude)

assert.Equal(t, test.out, got)
})
}
}
Loading

0 comments on commit 7d65071

Please sign in to comment.