constellation/coordinator/internal/selfactivation/selfactivation_test.go

286 lines
7.0 KiB
Go
Raw Normal View History

2022-06-21 09:56:16 +00:00
package selfactivation
import (
"context"
"errors"
"net"
"strconv"
"sync"
"testing"
"time"
"github.com/edgelesssys/constellation/activation/activationproto"
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
"github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/grpc/atlscredentials"
"github.com/edgelesssys/constellation/internal/grpc/dialer"
"github.com/edgelesssys/constellation/internal/grpc/testdialer"
"github.com/stretchr/testify/assert"
"go.uber.org/goleak"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
"google.golang.org/grpc"
testclock "k8s.io/utils/clock/testing"
)
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
func TestClient(t *testing.T) {
someErr := errors.New("failed")
peers := []cloudtypes.Instance{
{Role: role.Node, PrivateIPs: []string{"192.0.2.8"}},
{Role: role.Coordinator, PrivateIPs: []string{"192.0.2.1"}},
{Role: role.Coordinator, PrivateIPs: []string{"192.0.2.2", "192.0.2.3"}},
}
testCases := map[string]struct {
role role.Role
apiAnswers []any
setterAPI *stubActiveSetter
}{
"on node: metadata self: errors occur": {
role: role.Node,
apiAnswers: []any{
selfAnswer{err: someErr},
selfAnswer{err: someErr},
selfAnswer{err: someErr},
selfAnswer{instance: cloudtypes.Instance{Role: role.Node}},
listAnswer{instances: peers},
activateNodeAnswer{},
},
setterAPI: &stubActiveSetter{},
},
"on node: metadata self: no role in answer": {
role: role.Node,
apiAnswers: []any{
selfAnswer{},
selfAnswer{},
selfAnswer{},
selfAnswer{instance: cloudtypes.Instance{Role: role.Node}},
listAnswer{instances: peers},
activateNodeAnswer{},
},
setterAPI: &stubActiveSetter{},
},
"on node: metadata list: errors occur": {
role: role.Node,
apiAnswers: []any{
selfAnswer{instance: cloudtypes.Instance{Role: role.Node}},
listAnswer{err: someErr},
listAnswer{err: someErr},
listAnswer{err: someErr},
listAnswer{instances: peers},
activateNodeAnswer{},
},
setterAPI: &stubActiveSetter{},
},
"on node: metadata list: no coordinators in answer": {
role: role.Node,
apiAnswers: []any{
selfAnswer{instance: cloudtypes.Instance{Role: role.Node}},
listAnswer{},
listAnswer{},
listAnswer{},
listAnswer{instances: peers},
activateNodeAnswer{},
},
setterAPI: &stubActiveSetter{},
},
"on node: aaas ActivateNode: errors": {
role: role.Node,
apiAnswers: []any{
selfAnswer{instance: cloudtypes.Instance{Role: role.Node}},
listAnswer{instances: peers},
activateNodeAnswer{err: someErr},
listAnswer{instances: peers},
activateNodeAnswer{err: someErr},
listAnswer{instances: peers},
activateNodeAnswer{},
},
setterAPI: &stubActiveSetter{},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
clock := testclock.NewFakeClock(time.Now())
metadataAPI := newStubMetadataAPI()
netDialer := testdialer.NewBufconnDialer()
dialer := dialer.New(nil, nil, netDialer)
client := &SelfActivationClient{
timeout: 30 * time.Second,
interval: time.Millisecond,
dialer: dialer,
setterAPI: tc.setterAPI,
metadataAPI: metadataAPI,
clock: clock,
log: zaptest.NewLogger(t),
}
serverCreds := atlscredentials.New(nil, nil)
activationSever := grpc.NewServer(grpc.Creds(serverCreds))
activationAPI := newStubActivationServiceAPI()
activationproto.RegisterAPIServer(activationSever, activationAPI)
port := strconv.Itoa(constants.ActivationServicePort)
listener := netDialer.GetListener(net.JoinHostPort("192.0.2.3", port))
go activationSever.Serve(listener)
defer activationSever.GracefulStop()
client.Start()
for _, a := range tc.apiAnswers {
switch a := a.(type) {
case selfAnswer:
metadataAPI.selfAnswerC <- a
case listAnswer:
metadataAPI.listAnswerC <- a
case activateNodeAnswer:
activationAPI.activateNodeAnswerC <- a
}
clock.Step(time.Second)
}
client.Stop()
if tc.role == role.Node {
assert.Equal(1, tc.setterAPI.setNodeActiveCalled)
} else {
assert.Equal(1, tc.setterAPI.setCoordinatorActiveCalled)
}
})
}
}
func TestClientConcurrentStartStop(t *testing.T) {
client := &SelfActivationClient{
setterAPI: &stubActiveSetter{},
metadataAPI: &stubRepeaterMetadataAPI{},
clock: testclock.NewFakeClock(time.Now()),
log: zap.NewNop(),
}
wg := sync.WaitGroup{}
start := func() {
defer wg.Done()
client.Start()
}
stop := func() {
defer wg.Done()
client.Stop()
}
wg.Add(10)
go stop()
go start()
go start()
go stop()
go stop()
go start()
go start()
go stop()
go stop()
go start()
wg.Wait()
client.Stop()
}
type stubActiveSetter struct {
setNodeActiveErr error
setNodeActiveCalled int
setCoordinatorActiveErr error
setCoordinatorActiveCalled int
}
func (s *stubActiveSetter) SetNodeActive(_, _, _ []byte, _, _, _ string) error {
s.setNodeActiveCalled++
return s.setNodeActiveErr
}
func (s *stubActiveSetter) SetCoordinatorActive() error {
s.setCoordinatorActiveCalled++
return s.setCoordinatorActiveErr
}
type stubRepeaterMetadataAPI struct {
selfInstance cloudtypes.Instance
selfErr error
listInstances []cloudtypes.Instance
listErr error
}
func (s *stubRepeaterMetadataAPI) Self(_ context.Context) (cloudtypes.Instance, error) {
return s.selfInstance, s.selfErr
}
func (s *stubRepeaterMetadataAPI) List(_ context.Context) ([]cloudtypes.Instance, error) {
return s.listInstances, s.listErr
}
type stubMetadataAPI struct {
selfAnswerC chan selfAnswer
listAnswerC chan listAnswer
}
func newStubMetadataAPI() *stubMetadataAPI {
return &stubMetadataAPI{
selfAnswerC: make(chan selfAnswer),
listAnswerC: make(chan listAnswer),
}
}
func (s *stubMetadataAPI) Self(_ context.Context) (cloudtypes.Instance, error) {
answer := <-s.selfAnswerC
return answer.instance, answer.err
}
func (s *stubMetadataAPI) List(_ context.Context) ([]cloudtypes.Instance, error) {
answer := <-s.listAnswerC
return answer.instances, answer.err
}
type selfAnswer struct {
instance cloudtypes.Instance
err error
}
type listAnswer struct {
instances []cloudtypes.Instance
err error
}
type stubActivationServiceAPI struct {
activateNodeAnswerC chan activateNodeAnswer
activationproto.UnimplementedAPIServer
}
func newStubActivationServiceAPI() *stubActivationServiceAPI {
return &stubActivationServiceAPI{
activateNodeAnswerC: make(chan activateNodeAnswer),
}
}
func (s *stubActivationServiceAPI) ActivateWorkerNode(_ context.Context, _ *activationproto.ActivateWorkerNodeRequest,
) (*activationproto.ActivateWorkerNodeResponse, error) {
answer := <-s.activateNodeAnswerC
if answer.resp == nil {
answer.resp = &activationproto.ActivateWorkerNodeResponse{}
}
return answer.resp, answer.err
}
type activateNodeAnswer struct {
resp *activationproto.ActivateWorkerNodeResponse
err error
}