From 85e2022f1e680636d911ba77b873ba5b788c1260 Mon Sep 17 00:00:00 2001 From: Serge Logvinov Date: Sun, 5 May 2024 10:16:55 +0300 Subject: [PATCH] feat: node transformer feature flags Introduce feature flags: * PublicIPDiscovery enables the Cloud Controller Manager (CCM) to identify global/public IPs on the node. Signed-off-by: Serge Logvinov --- .github/workflows/build-test.yaml | 2 +- .golangci.yml | 7 +++-- pkg/talos/helper.go | 48 ++++++++++++++++++++----------- pkg/talos/helper_test.go | 39 ++++++++++++++++++++++++- pkg/talos/instances.go | 2 +- pkg/transformer/transformer.go | 20 +++++++++---- 6 files changed, 89 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 5d47899..0a10953 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -37,7 +37,7 @@ jobs: - name: Lint uses: golangci/golangci-lint-action@v5 with: - version: v1.57.1 + version: v1.58.0 args: --timeout=5m --config=.golangci.yml - name: Build timeout-minutes: 10 diff --git a/.golangci.yml b/.golangci.yml index 66bb42f..ef14cfa 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -143,16 +143,17 @@ linters: - errorlint - exhaustruct - exhaustivestruct + - err113 - forbidigo - forcetypeassert - funlen - - gas - gochecknoglobals - gochecknoinits - gocognit - godox - - goerr113 - gomnd + - gosec + - mnd - ifshort - ireturn # we return interfaces - maintidx @@ -175,7 +176,6 @@ linters: # temporarily disabled linters - copyloopvar - # https://github.com/golangci/golangci-lint/issues/4606 - intrange # abandoned linters for which golangci shows the warning that the repo is archived by the owner @@ -188,6 +188,7 @@ linters: - deadcode - ifshort - perfsprint + - execinquery disable-all: false fast: false diff --git a/pkg/talos/helper.go b/pkg/talos/helper.go index 9d0eebd..368d904 100644 --- a/pkg/talos/helper.go +++ b/pkg/talos/helper.go @@ -7,6 +7,7 @@ import ( "fmt" "strings" + "github.com/siderolabs/talos-cloud-controller-manager/pkg/transformer" utilsnet "github.com/siderolabs/talos-cloud-controller-manager/pkg/utils/net" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" @@ -21,29 +22,36 @@ import ( "k8s.io/utils/strings/slices" ) -func getNodeAddresses(config *cloudConfig, platform string, nodeIPs []string, ifaces []network.AddressStatusSpec) []v1.NodeAddress { - var publicIPv4s, publicIPv6s, publicIPs []string +func ipDescovery(nodeIPs []string, ifaces []network.AddressStatusSpec) (publicIPv4s, publicIPv6s []string) { + for _, iface := range ifaces { + if iface.LinkName == "kubespan" || iface.LinkName == "lo" { + continue + } - switch platform { - case "nocloud", "metal", "openstack": - for _, iface := range ifaces { - if iface.LinkName == "kubespan" { + ip := iface.Address.Addr() + if ip.IsGlobalUnicast() && !ip.IsPrivate() { + if slices.Contains(nodeIPs, ip.String()) { continue } - ip := iface.Address.Addr() - if ip.IsGlobalUnicast() && !ip.IsPrivate() { - if slices.Contains(nodeIPs, ip.String()) { - continue - } - - if ip.Is6() { - publicIPv6s = append(publicIPv6s, ip.String()) - } else { - publicIPv4s = append(publicIPv4s, ip.String()) - } + if ip.Is6() { + publicIPv6s = append(publicIPv6s, ip.String()) + } else { + publicIPv4s = append(publicIPv4s, ip.String()) } } + } + + return publicIPv4s, publicIPv6s +} + +func getNodeAddresses(config *cloudConfig, platform string, features *transformer.NodeFeaturesFlagSpec, nodeIPs []string, ifaces []network.AddressStatusSpec) []v1.NodeAddress { + var publicIPv4s, publicIPv6s, publicIPs []string + + switch platform { + // Those platforms don't expose public IPs information in metadata + case "nocloud", "metal", "openstack", "oracle": + publicIPv4s, publicIPv6s = ipDescovery(nodeIPs, ifaces) default: for _, iface := range ifaces { if iface.LinkName == "external" { @@ -62,6 +70,12 @@ func getNodeAddresses(config *cloudConfig, platform string, nodeIPs []string, if } } + if features != nil && features.PublicIPDiscovery { + ipv4, ipv6 := ipDescovery(nodeIPs, ifaces) + publicIPv4s = append(publicIPv4s, ipv4...) + publicIPv6s = append(publicIPv6s, ipv6...) + } + addresses := []v1.NodeAddress{} for _, ip := range utilsnet.PreferedDualStackNodeIPs(config.Global.PreferIPv6, nodeIPs) { addresses = append(addresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ip}) diff --git a/pkg/talos/helper_test.go b/pkg/talos/helper_test.go index 73bd8a2..7a9b816 100644 --- a/pkg/talos/helper_test.go +++ b/pkg/talos/helper_test.go @@ -30,6 +30,7 @@ func TestGetNodeAddresses(t *testing.T) { name string cfg cloudConfig platform string + features *transformer.NodeFeaturesFlagSpec providedIP string ifaces []network.AddressStatusSpec expected []v1.NodeAddress @@ -142,9 +143,45 @@ func TestGetNodeAddresses(t *testing.T) { {Type: v1.NodeExternalIP, Address: "2001:1234::1"}, }, }, + { + name: "gcp dualstack with public IPs", + cfg: cfg, + platform: "gcp", + providedIP: "192.168.0.1,fd15:1:2::192:168:0:1", + ifaces: []network.AddressStatusSpec{ + {Address: netip.MustParsePrefix("192.168.0.1/24")}, + {Address: netip.MustParsePrefix("fe80::e0b5:71ff:fe24:7e60/64")}, + {Address: netip.MustParsePrefix("1.2.3.4/24"), LinkName: "external"}, + {Address: netip.MustParsePrefix("2001:1234::123/64")}, + }, + expected: []v1.NodeAddress{ + {Type: v1.NodeInternalIP, Address: "192.168.0.1"}, + {Type: v1.NodeInternalIP, Address: "fd15:1:2:0:192:168:0:1"}, + {Type: v1.NodeExternalIP, Address: "1.2.3.4"}, + }, + }, + { + name: "gcp dualstack with public IPs and featureflag", + cfg: cfg, + platform: "gcp", + features: &transformer.NodeFeaturesFlagSpec{PublicIPDiscovery: true}, + providedIP: "192.168.0.1,fd15:1:2::192:168:0:1", + ifaces: []network.AddressStatusSpec{ + {Address: netip.MustParsePrefix("192.168.0.1/24")}, + {Address: netip.MustParsePrefix("fe80::e0b5:71ff:fe24:7e60/64")}, + {Address: netip.MustParsePrefix("1.2.3.4/24"), LinkName: "external"}, + {Address: netip.MustParsePrefix("2001:1234::123/64")}, + }, + expected: []v1.NodeAddress{ + {Type: v1.NodeInternalIP, Address: "192.168.0.1"}, + {Type: v1.NodeInternalIP, Address: "fd15:1:2:0:192:168:0:1"}, + {Type: v1.NodeExternalIP, Address: "1.2.3.4"}, + {Type: v1.NodeExternalIP, Address: "2001:1234::123"}, + }, + }, } { t.Run(tt.name, func(t *testing.T) { - addresses := getNodeAddresses(&tt.cfg, tt.platform, strings.Split(tt.providedIP, ","), tt.ifaces) + addresses := getNodeAddresses(&tt.cfg, tt.platform, tt.features, strings.Split(tt.providedIP, ","), tt.ifaces) assert.Equal(t, tt.expected, addresses) }) diff --git a/pkg/talos/instances.go b/pkg/talos/instances.go index 821e03b..32c421d 100644 --- a/pkg/talos/instances.go +++ b/pkg/talos/instances.go @@ -104,7 +104,7 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud return nil, fmt.Errorf("error getting interfaces list from the node %s: %w", node.Name, err) } - addresses := getNodeAddresses(i.c.config, meta.Platform, nodeIPs, ifaces) + addresses := getNodeAddresses(i.c.config, meta.Platform, &nodeSpec.Features, nodeIPs, ifaces) addresses = append(addresses, v1.NodeAddress{Type: v1.NodeHostName, Address: node.Name}) diff --git a/pkg/transformer/transformer.go b/pkg/transformer/transformer.go index fac8ac8..21032a6 100644 --- a/pkg/transformer/transformer.go +++ b/pkg/transformer/transformer.go @@ -20,12 +20,20 @@ type NodeTerm struct { Annotations map[string]string `yaml:"annotations,omitempty"` Labels map[string]string `yaml:"labels,omitempty"` PlatformMetadata map[string]string `yaml:"platformMetadata,omitempty"` + Features NodeFeaturesFlagSpec `yaml:"features,omitempty"` } // NodeSpec represents the transformed node specifcations. type NodeSpec struct { Annotations map[string]string Labels map[string]string + Features NodeFeaturesFlagSpec +} + +// NodeFeaturesFlagSpec represents the node features flags. +type NodeFeaturesFlagSpec struct { + // PublicIPDiscovery try to find public IP on the node + PublicIPDiscovery bool `yaml:"publicIPDiscovery,omitempty"` } var prohibitedPlatformMetadataKeys = []string{"hostname", "platform"} @@ -36,6 +44,11 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS return nil, nil } + node := &NodeSpec{ + Annotations: make(map[string]string), + Labels: make(map[string]string), + } + metadata := metadataFromStruct(platformMetadata) for _, term := range terms { @@ -45,11 +58,6 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS } if match { - node := &NodeSpec{ - Annotations: make(map[string]string), - Labels: make(map[string]string), - } - if term.Annotations != nil { for k, v := range term.Annotations { t, err := executeTemplate(v, platformMetadata) @@ -107,7 +115,7 @@ func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataS } } - return nil, nil + return node, nil } func executeTemplate(tmpl string, data interface{}) (string, error) {