mirror of
https://github.com/siderolabs/talos.git
synced 2025-10-29 15:31:12 +01:00
chore: support getting multiple endpoints from the Provision rpc call
The code will rotate through the endpoints, until it reaches the end, and only then it will try to do the provisioning again. Closes #7973 Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
This commit is contained in:
parent
dd45dd06cf
commit
ba827bf8b8
2
go.mod
2
go.mod
@ -124,7 +124,7 @@ require (
|
|||||||
github.com/siderolabs/grpc-proxy v0.4.0
|
github.com/siderolabs/grpc-proxy v0.4.0
|
||||||
github.com/siderolabs/kms-client v0.1.0
|
github.com/siderolabs/kms-client v0.1.0
|
||||||
github.com/siderolabs/net v0.4.0
|
github.com/siderolabs/net v0.4.0
|
||||||
github.com/siderolabs/siderolink v0.3.2
|
github.com/siderolabs/siderolink v0.3.3
|
||||||
github.com/siderolabs/talos/pkg/machinery v1.6.0-alpha.2
|
github.com/siderolabs/talos/pkg/machinery v1.6.0-alpha.2
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -691,8 +691,8 @@ github.com/siderolabs/net v0.4.0 h1:1bOgVay/ijPkJz4qct98nHsiB/ysLQU0KLoBC4qLm7I=
|
|||||||
github.com/siderolabs/net v0.4.0/go.mod h1:/ibG+Hm9HU27agp5r9Q3eZicEfjquzNzQNux5uEk0kM=
|
github.com/siderolabs/net v0.4.0/go.mod h1:/ibG+Hm9HU27agp5r9Q3eZicEfjquzNzQNux5uEk0kM=
|
||||||
github.com/siderolabs/protoenc v0.2.1 h1:BqxEmeWQeMpNP3R6WrPqDatX8sM/r4t97OP8mFmg6GA=
|
github.com/siderolabs/protoenc v0.2.1 h1:BqxEmeWQeMpNP3R6WrPqDatX8sM/r4t97OP8mFmg6GA=
|
||||||
github.com/siderolabs/protoenc v0.2.1/go.mod h1:StTHxjet1g11GpNAWiATgc8K0HMKiFSEVVFOa/H0otc=
|
github.com/siderolabs/protoenc v0.2.1/go.mod h1:StTHxjet1g11GpNAWiATgc8K0HMKiFSEVVFOa/H0otc=
|
||||||
github.com/siderolabs/siderolink v0.3.2 h1:ULFHQAgxtVCU7Sd+GLP7bDSQBXrwTtppaI4TKl/YqZc=
|
github.com/siderolabs/siderolink v0.3.3 h1:rnsN4K4TPtk38Ygs/oKQsiVe8iYUi9RRS8gh4U7mbGM=
|
||||||
github.com/siderolabs/siderolink v0.3.2/go.mod h1:juxlSF9cBzeBHsOjS7hVS3s0NDpC034i/OZunVReqmo=
|
github.com/siderolabs/siderolink v0.3.3/go.mod h1:juxlSF9cBzeBHsOjS7hVS3s0NDpC034i/OZunVReqmo=
|
||||||
github.com/siderolabs/tcpproxy v0.1.0 h1:IbkS9vRhjMOscc1US3M5P1RnsGKFgB6U5IzUk+4WkKA=
|
github.com/siderolabs/tcpproxy v0.1.0 h1:IbkS9vRhjMOscc1US3M5P1RnsGKFgB6U5IzUk+4WkKA=
|
||||||
github.com/siderolabs/tcpproxy v0.1.0/go.mod h1:onn6CPPj/w1UNqQ0U97oRPF0CqbrgEApYCw4P9IiCW8=
|
github.com/siderolabs/tcpproxy v0.1.0/go.mod h1:onn6CPPj/w1UNqQ0U97oRPF0CqbrgEApYCw4P9IiCW8=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
|||||||
@ -44,6 +44,7 @@ import (
|
|||||||
// ManagerController interacts with SideroLink API and brings up the SideroLink Wireguard interface.
|
// ManagerController interacts with SideroLink API and brings up the SideroLink Wireguard interface.
|
||||||
type ManagerController struct {
|
type ManagerController struct {
|
||||||
nodeKey wgtypes.Key
|
nodeKey wgtypes.Key
|
||||||
|
pd provisionData
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name implements controller.Controller interface.
|
// Name implements controller.Controller interface.
|
||||||
@ -146,28 +147,139 @@ func (ctrl *ManagerController) Run(ctx context.Context, r controller.Runtime, lo
|
|||||||
case <-r.EventCh():
|
case <-r.EventCh():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctrl.pd.IsEmpty() {
|
||||||
|
provision, err := ctrl.provision(ctx, r, logger)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error provisioning: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !provision.IsPresent() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl.pd = provision.ValueOrZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
serverAddress, err := netip.ParseAddr(ctrl.pd.ServerAddress)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing server address: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeAddress, err := netip.ParsePrefix(ctrl.pd.NodeAddressPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing node address: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
linkSpec := network.NewLinkSpec(network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.LinkID(constants.SideroLinkName)))
|
||||||
|
addressSpec := network.NewAddressSpec(network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.AddressID(constants.SideroLinkName, nodeAddress)))
|
||||||
|
|
||||||
|
// Rotate through the endpoints.
|
||||||
|
ep, ok := ctrl.pd.TakeEndpoint()
|
||||||
|
if !ok {
|
||||||
|
return errors.New("host returned no endpoints")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(
|
||||||
|
"configuring siderolink connection",
|
||||||
|
zap.String("peer_endpoint", ep),
|
||||||
|
zap.String("next_peer_endpoint", ctrl.pd.PeekNextEndpoint()),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := safe.WriterModify(ctx, r, linkSpec,
|
||||||
|
func(res *network.LinkSpec) error {
|
||||||
|
spec := res.TypedSpec()
|
||||||
|
|
||||||
|
spec.ConfigLayer = network.ConfigOperator
|
||||||
|
spec.Name = constants.SideroLinkName
|
||||||
|
spec.Type = nethelpers.LinkNone
|
||||||
|
spec.Kind = "wireguard"
|
||||||
|
spec.Up = true
|
||||||
|
spec.Logical = true
|
||||||
|
spec.MTU = wireguard.LinkMTU
|
||||||
|
|
||||||
|
spec.Wireguard = network.WireguardSpec{
|
||||||
|
PrivateKey: ctrl.nodeKey.String(),
|
||||||
|
Peers: []network.WireguardPeer{
|
||||||
|
{
|
||||||
|
PublicKey: ctrl.pd.ServerPublicKey,
|
||||||
|
Endpoint: ep,
|
||||||
|
AllowedIPs: []netip.Prefix{
|
||||||
|
netip.PrefixFrom(serverAddress, serverAddress.BitLen()),
|
||||||
|
},
|
||||||
|
// make sure Talos pings SideroLink endpoint, so that tunnel is established:
|
||||||
|
// SideroLink doesn't know Talos endpoint.
|
||||||
|
PersistentKeepaliveInterval: constants.SideroLinkDefaultPeerKeepalive,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
spec.Wireguard.Sort()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("error creating siderolink spec: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := safe.WriterModify(ctx, r, addressSpec,
|
||||||
|
func(res *network.AddressSpec) error {
|
||||||
|
spec := res.TypedSpec()
|
||||||
|
|
||||||
|
spec.ConfigLayer = network.ConfigOperator
|
||||||
|
spec.Address = nodeAddress
|
||||||
|
spec.Family = nethelpers.FamilyInet6
|
||||||
|
spec.Flags = nethelpers.AddressFlags(nethelpers.AddressPermanent)
|
||||||
|
spec.LinkName = constants.SideroLinkName
|
||||||
|
spec.Scope = nethelpers.ScopeGlobal
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("error creating address spec: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keepLinkSpecSet := map[resource.ID]struct{}{
|
||||||
|
linkSpec.Metadata().ID(): {},
|
||||||
|
}
|
||||||
|
|
||||||
|
keepAddressSpecSet := map[resource.ID]struct{}{
|
||||||
|
addressSpec.Metadata().ID(): {},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctrl.cleanup(ctx, r, keepLinkSpecSet, keepAddressSpecSet, logger); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(
|
||||||
|
"siderolink connection configured",
|
||||||
|
zap.String("endpoint", ctrl.pd.apiEndpont),
|
||||||
|
zap.String("node_uuid", ctrl.pd.nodeUUID),
|
||||||
|
zap.String("node_address", nodeAddress.String()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func (ctrl *ManagerController) provision(ctx context.Context, r controller.Runtime, logger *zap.Logger) (optional.Optional[provisionData], error) {
|
||||||
cfg, err := safe.ReaderGetByID[*siderolink.Config](ctx, r, siderolink.ConfigID)
|
cfg, err := safe.ReaderGetByID[*siderolink.Config](ctx, r, siderolink.ConfigID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if state.IsNotFoundError(err) {
|
if state.IsNotFoundError(err) {
|
||||||
if cleanupErr := ctrl.cleanup(ctx, r, nil, nil, logger); cleanupErr != nil {
|
if cleanupErr := ctrl.cleanup(ctx, r, nil, nil, logger); cleanupErr != nil {
|
||||||
return fmt.Errorf("failed to do cleanup: %w", cleanupErr)
|
return optional.None[provisionData](), fmt.Errorf("failed to do cleanup: %w", cleanupErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// no config
|
// no config
|
||||||
continue
|
return optional.None[provisionData](), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("failed to get siderolink config: %w", err)
|
return optional.None[provisionData](), fmt.Errorf("failed to get siderolink config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sysInfo, err := safe.ReaderGetByID[*hardware.SystemInformation](ctx, r, hardware.SystemInformationID)
|
sysInfo, err := safe.ReaderGetByID[*hardware.SystemInformation](ctx, r, hardware.SystemInformationID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if state.IsNotFoundError(err) {
|
if state.IsNotFoundError(err) {
|
||||||
// no system information
|
// no system information
|
||||||
continue
|
return optional.None[provisionData](), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("failed to get system information: %w", err)
|
return optional.None[provisionData](), fmt.Errorf("failed to get system information: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeUUID := sysInfo.TypedSpec().UUID
|
nodeUUID := sysInfo.TypedSpec().UUID
|
||||||
@ -175,7 +287,7 @@ func (ctrl *ManagerController) Run(ctx context.Context, r controller.Runtime, lo
|
|||||||
|
|
||||||
parsedEndpoint, err := endpoint.Parse(stringEndpoint)
|
parsedEndpoint, err := endpoint.Parse(stringEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse siderolink endpoint: %w", err)
|
return optional.None[provisionData](), fmt.Errorf("failed to parse siderolink endpoint: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var transportCredentials credentials.TransportCredentials
|
var transportCredentials credentials.TransportCredentials
|
||||||
@ -230,91 +342,49 @@ func (ctrl *ManagerController) Run(ctx context.Context, r controller.Runtime, lo
|
|||||||
|
|
||||||
resp, err := provision()
|
resp, err := provision()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return optional.None[provisionData](), err
|
||||||
}
|
}
|
||||||
|
|
||||||
serverAddress, err := netip.ParseAddr(resp.ServerAddress)
|
return optional.Some(provisionData{
|
||||||
if err != nil {
|
nodeUUID: nodeUUID,
|
||||||
return fmt.Errorf("error parsing server address: %w", err)
|
apiEndpont: stringEndpoint,
|
||||||
|
ServerAddress: resp.ServerAddress,
|
||||||
|
ServerPublicKey: resp.ServerPublicKey,
|
||||||
|
NodeAddressPrefix: resp.NodeAddressPrefix,
|
||||||
|
endpoints: resp.GetEndpoints(),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type provisionData struct {
|
||||||
|
nodeUUID string
|
||||||
|
apiEndpont string
|
||||||
|
ServerAddress string
|
||||||
|
ServerPublicKey string
|
||||||
|
NodeAddressPrefix string
|
||||||
|
endpoints []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *provisionData) IsEmpty() bool {
|
||||||
|
return d == nil || len(d.endpoints) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *provisionData) TakeEndpoint() (string, bool) {
|
||||||
|
if d.IsEmpty() {
|
||||||
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeAddress, err := netip.ParsePrefix(resp.NodeAddressPrefix)
|
ep := d.endpoints[0]
|
||||||
if err != nil {
|
d.endpoints = d.endpoints[1:]
|
||||||
return fmt.Errorf("error parsing node address: %w", err)
|
|
||||||
|
return ep, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *provisionData) PeekNextEndpoint() string {
|
||||||
|
if d.IsEmpty() {
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
linkSpec := network.NewLinkSpec(network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.LinkID(constants.SideroLinkName)))
|
return d.endpoints[0]
|
||||||
addressSpec := network.NewAddressSpec(network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.AddressID(constants.SideroLinkName, nodeAddress)))
|
|
||||||
|
|
||||||
if err := safe.WriterModify(ctx, r, linkSpec,
|
|
||||||
func(res *network.LinkSpec) error {
|
|
||||||
spec := res.TypedSpec()
|
|
||||||
|
|
||||||
spec.ConfigLayer = network.ConfigOperator
|
|
||||||
spec.Name = constants.SideroLinkName
|
|
||||||
spec.Type = nethelpers.LinkNone
|
|
||||||
spec.Kind = "wireguard"
|
|
||||||
spec.Up = true
|
|
||||||
spec.Logical = true
|
|
||||||
spec.MTU = wireguard.LinkMTU
|
|
||||||
|
|
||||||
spec.Wireguard = network.WireguardSpec{
|
|
||||||
PrivateKey: ctrl.nodeKey.String(),
|
|
||||||
Peers: []network.WireguardPeer{
|
|
||||||
{
|
|
||||||
PublicKey: resp.ServerPublicKey,
|
|
||||||
Endpoint: resp.ServerEndpoint,
|
|
||||||
AllowedIPs: []netip.Prefix{
|
|
||||||
netip.PrefixFrom(serverAddress, serverAddress.BitLen()),
|
|
||||||
},
|
|
||||||
// make sure Talos pings SideroLink endpoint, so that tunnel is established:
|
|
||||||
// SideroLink doesn't know Talos endpoint.
|
|
||||||
PersistentKeepaliveInterval: constants.SideroLinkDefaultPeerKeepalive,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
spec.Wireguard.Sort()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("error creating siderolink spec: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := safe.WriterModify(ctx, r, addressSpec,
|
|
||||||
func(res *network.AddressSpec) error {
|
|
||||||
spec := res.TypedSpec()
|
|
||||||
|
|
||||||
spec.ConfigLayer = network.ConfigOperator
|
|
||||||
spec.Address = nodeAddress
|
|
||||||
spec.Family = nethelpers.FamilyInet6
|
|
||||||
spec.Flags = nethelpers.AddressFlags(nethelpers.AddressPermanent)
|
|
||||||
spec.LinkName = constants.SideroLinkName
|
|
||||||
spec.Scope = nethelpers.ScopeGlobal
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("error creating address spec: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
keepLinkSpecSet := map[resource.ID]struct{}{
|
|
||||||
linkSpec.Metadata().ID(): {},
|
|
||||||
}
|
|
||||||
|
|
||||||
keepAddressSpecSet := map[resource.ID]struct{}{
|
|
||||||
addressSpec.Metadata().ID(): {},
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ctrl.cleanup(ctx, r, keepLinkSpecSet, keepAddressSpecSet, logger); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info(
|
|
||||||
"siderolink connection configured",
|
|
||||||
zap.String("endpoint", stringEndpoint),
|
|
||||||
zap.String("node_uuid", nodeUUID),
|
|
||||||
zap.String("node_address", nodeAddress.String()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctrl *ManagerController) cleanup(
|
func (ctrl *ManagerController) cleanup(
|
||||||
|
|||||||
@ -72,7 +72,7 @@ const (
|
|||||||
|
|
||||||
func (srv mockServer) Provision(_ context.Context, _ *pb.ProvisionRequest) (*pb.ProvisionResponse, error) {
|
func (srv mockServer) Provision(_ context.Context, _ *pb.ProvisionRequest) (*pb.ProvisionResponse, error) {
|
||||||
return &pb.ProvisionResponse{
|
return &pb.ProvisionResponse{
|
||||||
ServerEndpoint: mockServerEndpoint,
|
ServerEndpoint: pb.MakeEndpoints(mockServerEndpoint),
|
||||||
ServerAddress: mockServerAddress,
|
ServerAddress: mockServerAddress,
|
||||||
ServerPublicKey: mockServerPublicKey,
|
ServerPublicKey: mockServerPublicKey,
|
||||||
NodeAddressPrefix: mockNodeAddressPrefix,
|
NodeAddressPrefix: mockNodeAddressPrefix,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user