Skip to content

Commit

Permalink
feat: implement route network controllers
Browse files Browse the repository at this point in the history
Route handling is very similar to addresses:

* `RouteStatus` describes kernel routing table state,
`RouteStatusController` reflects kernel state into resources
* `RouteSpec` defines routes to be configured
* `RouteConfigController` creates `RouteSpec`s based on cmdline and
machine configuration
* `RouteMergeController` merges different configuration layers into the
final representation
* `RouteSpecController` applies the specs to the kernel routing table

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
  • Loading branch information
smira authored and talos-bot committed May 25, 2021
1 parent f5bf88a commit 0acb04a
Show file tree
Hide file tree
Showing 39 changed files with 2,338 additions and 79 deletions.
2 changes: 1 addition & 1 deletion cmd/talosctl/cmd/talos/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Talos etcd cluster can be recovered from a known snapshot with '--recover-from='
return fmt.Errorf("error opening snapshot file: %w", err)
}

defer snapshot.Close() //nolint: errcheck
defer snapshot.Close() //nolint:errcheck

_, err = c.EtcdRecover(ctx, snapshot)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1844,13 +1844,13 @@ func (s *Server) EtcdRecover(srv machine.MachineService_EtcdRecoverServer) error
return fmt.Errorf("error creating etcd recovery snapshot: %w", err)
}

defer snapshot.Close() //nolint: errcheck
defer snapshot.Close() //nolint:errcheck

successfulUpload := false

defer func() {
if !successfulUpload {
os.Remove(snapshot.Name()) //nolint: errcheck
os.Remove(snapshot.Name()) //nolint:errcheck
}
}()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func (ctrl *ExtraManifestController) processInline(ctx context.Context, r contro
return nil
}

//nolint: dupl
//nolint:dupl
func (ctrl *ExtraManifestController) teardownAll(ctx context.Context, r controller.Runtime) error {
manifests, err := r.List(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "", resource.VersionUndefined))
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/app/machined/pkg/controllers/k8s/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func (ctrl *ManifestController) render(cfg config.K8sManifestsSpec, scrt *secret
return manifests, nil
}

//nolint: dupl
//nolint:dupl
func (ctrl *ManifestController) teardownAll(ctx context.Context, r controller.Runtime) error {
manifests, err := r.List(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "", resource.VersionUndefined))
if err != nil {
Expand Down
70 changes: 13 additions & 57 deletions internal/app/machined/pkg/controllers/network/address_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ package network
import (
"context"
"fmt"
"net"
"sort"
"strings"

"github.com/AlekSi/pointer"
"github.com/cosi-project/runtime/pkg/controller"
Expand Down Expand Up @@ -59,7 +56,7 @@ func (ctrl *AddressConfigController) Outputs() []controller.Output {

// Run implements controller.Controller interface.
//
//nolint: gocyclo, cyclop
//nolint:gocyclo,cyclop
func (ctrl *AddressConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
// apply defaults for the loopback interface once
defaultTouchedIDs, err := ctrl.apply(ctx, r, ctrl.loopbackDefaults())
Expand Down Expand Up @@ -141,6 +138,11 @@ func (ctrl *AddressConfigController) Run(ctx context.Context, r controller.Runti
}

for _, res := range list.Items {
if res.Metadata().Owner() != ctrl.Name() {
// skip specs created by other controllers
continue
}

if _, ok := touchedIDs[res.Metadata().ID()]; !ok {
if err = r.Destroy(ctx, res.Metadata()); err != nil {
return fmt.Errorf("error cleaning up addresses: %w", err)
Expand All @@ -150,6 +152,7 @@ func (ctrl *AddressConfigController) Run(ctx context.Context, r controller.Runti
}
}

//nolint:dupl
func (ctrl *AddressConfigController) apply(ctx context.Context, r controller.Runtime, addresses []network.AddressSpecSpec) ([]resource.ID, error) {
ids := make([]string, 0, len(addresses))

Expand Down Expand Up @@ -202,52 +205,23 @@ func (ctrl *AddressConfigController) loopbackDefaults() []network.AddressSpecSpe
}
}

//nolint: gocyclo
func (ctrl *AddressConfigController) parseCmdline(logger *zap.Logger) (address network.AddressSpecSpec) {
if ctrl.Cmdline == nil {
return
}

cmdline := ctrl.Cmdline.Get("ip").First()
if cmdline == nil {
return
}

// https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt
// ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>:<ntp0-ip>
fields := strings.Split(*cmdline, ":")

// If dhcp is specified, we'll handle it as a normal discovered
// interface
if len(fields) == 1 && fields[0] == "dhcp" {
return
}

var err error

address.Address.IP, err = netaddr.ParseIP(fields[0])
settings, err := ParseCmdlineNetwork(ctrl.Cmdline)
if err != nil {
logger.Info("ignoring cmdline address parse failure", zap.Error(err))
logger.Info("ignoring cmdline parse failure", zap.Error(err))

return
}

if len(fields) >= 4 {
netmask, err := netaddr.ParseIP(fields[3])
if err != nil {
logger.Info("ignoring cmdline netmask parse failure", zap.Error(err))

return
}

ones, _ := net.IPMask(netmask.IPAddr().IP).Size()

address.Address.Bits = uint8(ones)
} else {
// default is to have complete address masked
address.Address.Bits = address.Address.IP.BitLen()
if settings.Address.IsZero() {
return
}

address.Address = settings.Address
if address.Address.IP.Is6() {
address.Family = nethelpers.FamilyInet6
} else {
Expand All @@ -256,26 +230,8 @@ func (ctrl *AddressConfigController) parseCmdline(logger *zap.Logger) (address n

address.Scope = nethelpers.ScopeGlobal
address.Flags = nethelpers.AddressFlags(nethelpers.AddressPermanent)

address.Layer = network.ConfigCmdline

if len(fields) >= 6 {
address.LinkName = fields[5]
} else {
ifaces, _ := net.Interfaces() //nolint: errcheck // ignoring error here as ifaces will be empty

sort.Slice(ifaces, func(i, j int) bool { return ifaces[i].Name < ifaces[j].Name })

for _, iface := range ifaces {
if iface.Flags&net.FlagLoopback != 0 {
continue
}

address.LinkName = iface.Name

break
}
}
address.LinkName = settings.LinkName

return address
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func (suite *AddressConfigSuite) TestCmdlineNoNetmask() {

suite.startRuntime()

ifaces, _ := net.Interfaces() //nolint: errcheck // ignoring error here as ifaces will be empty
ifaces, _ := net.Interfaces() //nolint:errcheck // ignoring error here as ifaces will be empty

sort.Slice(ifaces, func(i, j int) bool { return ifaces[i].Name < ifaces[j].Name })

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +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/.

// Package network provides controllers which manage network resources.
package network

import (
Expand Down Expand Up @@ -54,7 +55,7 @@ func (ctrl *AddressMergeController) Outputs() []controller.Output {

// Run implements controller.Controller interface.
//
//nolint: gocyclo
//nolint:gocyclo
func (ctrl *AddressMergeController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
for {
select {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (ctrl *AddressSpecController) Outputs() []controller.Output {

// Run implements controller.Controller interface.
//
//nolint: gocyclo
//nolint:gocyclo,dupl
func (ctrl *AddressSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
watchCh := make(chan struct{})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (suite *AddressSpecSuite) assertLinkAddress(linkName, address string) error
conn, err := rtnetlink.Dial(nil)
suite.Require().NoError(err)

defer conn.Close() //nolint: errcheck
defer conn.Close() //nolint:errcheck

linkAddresses, err := conn.Address.List()
suite.Require().NoError(err)
Expand Down Expand Up @@ -110,7 +110,7 @@ func (suite *AddressSpecSuite) assertNoLinkAddress(linkName, address string) err
conn, err := rtnetlink.Dial(nil)
suite.Require().NoError(err)

defer conn.Close() //nolint: errcheck
defer conn.Close() //nolint:errcheck

linkAddresses, err := conn.Address.List()
suite.Require().NoError(err)
Expand Down Expand Up @@ -200,7 +200,7 @@ func (suite *AddressSpecSuite) TestDummy() {
iface, err := net.InterfaceByName(dummyInterface)
suite.Require().NoError(err)

defer conn.Link.Delete(uint32(iface.Index)) //nolint: errcheck
defer conn.Link.Delete(uint32(iface.Index)) //nolint:errcheck

suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
func() error {
Expand Down
127 changes: 127 additions & 0 deletions internal/app/machined/pkg/controllers/network/cmdline.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// 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

import (
"fmt"
"net"
"sort"
"strings"

"github.com/talos-systems/go-procfs/procfs"
"inet.af/netaddr"
)

// CmdlineNetworking contains parsed cmdline networking settings.
type CmdlineNetworking struct {
DHCP bool
Address netaddr.IPPrefix
Gateway netaddr.IP
Hostname string
LinkName string
DNSAddresses []netaddr.IP
NTPAddresses []netaddr.IP
}

// ParseCmdlineNetwork parses `ip=` kernel cmdline argument producing all the available configuration options.
//
//nolint:gocyclo,cyclop
func ParseCmdlineNetwork(cmdline *procfs.Cmdline) (CmdlineNetworking, error) {
var (
settings CmdlineNetworking
err error
)

ipSettings := cmdline.Get("ip").First()
if ipSettings == nil {
return settings, nil
}

// https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt
// ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>:<ntp0-ip>
fields := strings.Split(*ipSettings, ":")

// If dhcp is specified, we'll handle it as a normal discovered
// interface
if len(fields) == 1 && fields[0] == "dhcp" {
settings.DHCP = true
}

if !settings.DHCP {
for i := range fields {
if fields[i] == "" {
continue
}

switch i {
case 0:
settings.Address.IP, err = netaddr.ParseIP(fields[0])
if err != nil {
return settings, fmt.Errorf("cmdline address parse failure: %s", err)
}

// default is to have complete address masked
settings.Address.Bits = settings.Address.IP.BitLen()
case 2:
settings.Gateway, err = netaddr.ParseIP(fields[2])
if err != nil {
return settings, fmt.Errorf("cmdline gateway parse failure: %s", err)
}
case 3:
var netmask netaddr.IP

netmask, err = netaddr.ParseIP(fields[3])
if err != nil {
return settings, fmt.Errorf("cmdline netmask parse failure: %s", err)
}

ones, _ := net.IPMask(netmask.IPAddr().IP).Size()

settings.Address.Bits = uint8(ones)
case 4:
settings.Hostname = fields[4]
case 5:
settings.LinkName = fields[5]
case 7, 8:
var dnsIP netaddr.IP

dnsIP, err = netaddr.ParseIP(fields[i])
if err != nil {
return settings, fmt.Errorf("error parsing DNS IP: %w", err)
}

settings.DNSAddresses = append(settings.DNSAddresses, dnsIP)
case 9:
var ntpIP netaddr.IP

ntpIP, err = netaddr.ParseIP(fields[i])
if err != nil {
return settings, fmt.Errorf("error parsing DNS IP: %w", err)
}

settings.NTPAddresses = append(settings.NTPAddresses, ntpIP)
}
}
}

// if interface name is not set, pick the first non-loopback interface
if settings.LinkName == "" {
ifaces, _ := net.Interfaces() //nolint:errcheck // ignoring error here as ifaces will be empty

sort.Slice(ifaces, func(i, j int) bool { return ifaces[i].Name < ifaces[j].Name })

for _, iface := range ifaces {
if iface.Flags&net.FlagLoopback != 0 {
continue
}

settings.LinkName = iface.Name

break
}
}

return settings, nil
}
Loading

0 comments on commit 0acb04a

Please sign in to comment.