Dmitriy Matrenichev ba827bf8b8
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>
2023-11-25 21:38:44 +03:00

177 lines
5.9 KiB
Go

// 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 siderolink_test
import (
"context"
"fmt"
"net"
"net/netip"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
"github.com/siderolabs/go-procfs/procfs"
"github.com/siderolabs/go-retry/retry"
pb "github.com/siderolabs/siderolink/api/siderolink"
"github.com/stretchr/testify/suite"
"google.golang.org/grpc"
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
siderolinkctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/siderolink"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
"github.com/siderolabs/talos/pkg/machinery/resources/config"
"github.com/siderolabs/talos/pkg/machinery/resources/hardware"
"github.com/siderolabs/talos/pkg/machinery/resources/network"
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
"github.com/siderolabs/talos/pkg/machinery/resources/siderolink"
)
func TestManagerSuite(t *testing.T) {
var m ManagerSuite
m.AfterSetup = func(suite *ctest.DefaultSuite) {
lis, err := net.Listen("tcp", "localhost:0")
suite.Require().NoError(err)
m.s = grpc.NewServer()
pb.RegisterProvisionServiceServer(m.s, mockServer{})
go func() {
suite.Require().NoError(m.s.Serve(lis))
}()
cmdline := procfs.NewCmdline(fmt.Sprintf("%s=%s", constants.KernelParamSideroLink, lis.Addr().String()))
configController := siderolinkctrl.ConfigController{Cmdline: cmdline}
suite.Require().NoError(suite.Runtime().RegisterController(&siderolinkctrl.ManagerController{}))
suite.Require().NoError(suite.Runtime().RegisterController(&configController))
}
suite.Run(t, &m)
}
type ManagerSuite struct {
ctest.DefaultSuite
s *grpc.Server
}
type mockServer struct {
pb.UnimplementedProvisionServiceServer
}
const (
mockServerEndpoint = "127.0.0.11:51820"
mockServerAddress = "fdae:41e4:649b:9303:b6db:d99c:215e:dfc4"
mockServerPublicKey = "2aq/V91QyrHAoH24RK0bldukgo2rWk+wqE5Eg6TArCM="
mockNodeAddressPrefix = "fdae:41e4:649b:9303:2a07:9c7:5b08:aef7/64"
)
func (srv mockServer) Provision(_ context.Context, _ *pb.ProvisionRequest) (*pb.ProvisionResponse, error) {
return &pb.ProvisionResponse{
ServerEndpoint: pb.MakeEndpoints(mockServerEndpoint),
ServerAddress: mockServerAddress,
ServerPublicKey: mockServerPublicKey,
NodeAddressPrefix: mockNodeAddressPrefix,
}, nil
}
func (suite *ManagerSuite) TestReconcile() {
networkStatus := network.NewStatus(network.NamespaceName, network.StatusID)
networkStatus.TypedSpec().AddressReady = true
suite.Require().NoError(suite.State().Create(suite.Ctx(), networkStatus))
systemInformation := hardware.NewSystemInformation(hardware.SystemInformationID)
systemInformation.TypedSpec().UUID = "71233efd-7a07-43f8-b6ba-da90fae0e88b"
suite.Require().NoError(suite.State().Create(suite.Ctx(), systemInformation))
uniqToken := runtime.NewUniqueMachineToken()
uniqToken.TypedSpec().Token = "random-token"
suite.Require().NoError(suite.State().Create(suite.Ctx(), uniqToken))
nodeAddress := netip.MustParsePrefix(mockNodeAddressPrefix)
addressSpec := network.NewAddressSpec(network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.AddressID(constants.SideroLinkName, nodeAddress)))
linkSpec := network.NewLinkSpec(network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.LinkID(constants.SideroLinkName)))
suite.AssertWithin(10*time.Second, 100*time.Millisecond, func() error {
addressResource, err := ctest.Get[*network.AddressSpec](suite, addressSpec.Metadata())
if err != nil {
if state.IsNotFoundError(err) {
return retry.ExpectedError(err)
}
return err
}
address := addressResource.TypedSpec()
suite.Assert().Equal(nodeAddress, address.Address)
suite.Assert().Equal(network.ConfigOperator, address.ConfigLayer)
suite.Assert().Equal(nethelpers.FamilyInet6, address.Family)
suite.Assert().Equal(constants.SideroLinkName, address.LinkName)
linkResource, err := ctest.Get[*network.LinkSpec](suite, linkSpec.Metadata())
if err != nil {
if state.IsNotFoundError(err) {
return retry.ExpectedError(err)
}
return err
}
link := linkResource.TypedSpec()
suite.Assert().Equal("wireguard", link.Kind)
suite.Assert().Equal(network.ConfigOperator, link.ConfigLayer)
suite.Assert().NotEmpty(link.Wireguard.PrivateKey)
suite.Assert().Len(link.Wireguard.Peers, 1)
suite.Assert().Equal(mockServerEndpoint, link.Wireguard.Peers[0].Endpoint)
suite.Assert().Equal(mockServerPublicKey, link.Wireguard.Peers[0].PublicKey)
suite.Assert().Equal(
[]netip.Prefix{
netip.PrefixFrom(
netip.MustParseAddr(mockServerAddress),
128,
),
}, link.Wireguard.Peers[0].AllowedIPs,
)
suite.Assert().Equal(
constants.SideroLinkDefaultPeerKeepalive,
link.Wireguard.Peers[0].PersistentKeepaliveInterval,
)
return nil
})
// remove config
configPtr := siderolink.NewConfig(config.NamespaceName, siderolink.ConfigID).Metadata()
destroyErr := suite.State().Destroy(suite.Ctx(), configPtr,
state.WithDestroyOwner(pointer.To(siderolinkctrl.ConfigController{}).Name()))
suite.Require().NoError(destroyErr)
suite.AssertWithin(10*time.Second, 100*time.Millisecond, func() error {
_, err := ctest.Get[*network.LinkSpec](suite, linkSpec.Metadata())
if err == nil {
return retry.ExpectedError(fmt.Errorf("link resource still exists"))
}
suite.Assert().Truef(state.IsNotFoundError(err), "unexpected error: %v", err)
_, err = ctest.Get[*network.AddressSpec](suite, addressSpec.Metadata())
if err == nil {
return retry.ExpectedError(fmt.Errorf("address resource still exists"))
}
suite.Assert().Truef(state.IsNotFoundError(err), "unexpected error: %v", err)
return nil
})
}