Files
go-sip/internal/rpc/server_test.go
T

165 lines
7.7 KiB
Go

package rpc
import (
"context"
"crypto/tls"
"crypto/x509"
"net"
"testing"
"time"
agentpb "git.ipao.vip/rogee/go-sip/gen/agent"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
"google.golang.org/grpc/test/bufconn"
"reflect"
)
func TestPeerCertificateAllowlist(t *testing.T) {
_, allowedCert, _ := testCertificate(t, nil, nil, false, []string{"dispatcher.local"}, nil)
fingerprint := CertificateFingerprint(allowedCert)
server := NewServer(ServerOptions{RequirePeerCertificate: true, PeerCertificateFingerprints: map[string]struct{}{fingerprint: {}}})
allowedContext := peer.NewContext(context.Background(), &peer.Peer{AuthInfo: credentials.TLSInfo{State: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{{allowedCert}}}}})
if err := server.checkPeer(allowedContext, "agent-1"); err != nil {
t.Fatal(err)
}
_, rejectedCert, _ := testCertificate(t, nil, nil, false, []string{"other-dispatcher.local"}, nil)
rejectedContext := peer.NewContext(context.Background(), &peer.Peer{AuthInfo: credentials.TLSInfo{State: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{{rejectedCert}}}}})
if err := server.checkPeer(rejectedContext, "agent-1"); status.Code(err) != codes.PermissionDenied {
t.Fatalf("got %v, want PermissionDenied", err)
}
}
func TestGetAgentStatusSupportsPreActivationProbe(t *testing.T) {
now := time.Unix(100, 0)
server := NewServer(ServerOptions{
Now: func() time.Time { return now },
Status: &agentpb.AgentStatus{
AgentId: "agent-1",
CellId: "cell-1",
BootId: "boot-current",
SoftwareVersion: "test",
ProtocolVersion: "agent.v1",
AdmissionState: agentpb.AdmissionState_ADMISSION_STATE_CLOSED,
},
})
response, err := server.GetAgentStatus(context.Background(), &agentpb.GetAgentStatusRequest{
Meta: &agentpb.RequestMeta{ProtocolVersion: "agent.v1", RequestId: "probe-request", TraceId: "probe-trace", OperationId: "probe-operation", AgentId: "agent-1", CellId: "cell-1"},
Target: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1"},
})
require.NoError(t, err)
require.Equal(t, "boot-current", response.Status.BootId)
require.Equal(t, false, response.Status.SessionActive)
require.Equal(t, false, response.Status.MtlsAuthenticated)
active := activatedServer(now, t)
activeResponse, err := active.GetAgentStatus(context.Background(), &agentpb.GetAgentStatusRequest{Meta: testMeta("status-active", "status-active-key", 1), Target: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1"}})
require.NoError(t, err)
require.Equal(t, true, activeResponse.Status.SessionActive)
}
func TestAgentIdentityIsBoundToConfiguredEndpoint(t *testing.T) {
server := NewServer(ServerOptions{Status: &agentpb.AgentStatus{AgentId: "agent-1", CellId: "cell-1"}})
_, err := server.GetAgentStatus(context.Background(), &agentpb.GetAgentStatusRequest{
Meta: &agentpb.RequestMeta{ProtocolVersion: "agent.v1", RequestId: "wrong-probe", TraceId: "wrong-probe", OperationId: "wrong-probe", AgentId: "agent-2", CellId: "cell-2"},
Target: &agentpb.AgentBinding{AgentId: "agent-2", CellId: "cell-2"},
})
require.Equal(t, codes.PermissionDenied, status.Code(err))
_, err = server.ActivateAgent(context.Background(), &agentpb.ActivateAgentRequest{
Meta: &agentpb.RequestMeta{ProtocolVersion: "agent.v1", RequestId: "wrong-activate", TraceId: "wrong-activate", OperationId: "wrong-activate", AgentId: "agent-2", CellId: "cell-2", BootId: "boot-2", DispatcherEpoch: "epoch-2"},
Binding: &agentpb.AgentBinding{AgentId: "agent-2", CellId: "cell-2", ExpectedBootId: "boot-2", DispatcherEpoch: "epoch-2", SessionGeneration: 1},
ActivationOperationId: "wrong-activate",
})
require.Equal(t, codes.PermissionDenied, status.Code(err))
}
func TestSessionGenerationFencesOlderRequests(t *testing.T) {
now := time.Unix(100, 0)
server := NewServer(ServerOptions{Now: func() time.Time { return now }})
firstMeta := testMeta("activate-1", "", 0)
firstMeta.OperationId = "activate-1"
_, err := server.ActivateAgent(context.Background(), &agentpb.ActivateAgentRequest{
Meta: firstMeta,
Binding: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1", ExpectedBootId: "boot-1", DispatcherEpoch: "epoch-1", SessionGeneration: 1},
ActivationOperationId: "activate-1",
})
require.NoError(t, err)
secondMeta := testMeta("activate-2", "", 0)
secondMeta.BootId = "boot-2"
secondMeta.OperationId = "activate-2"
_, err = server.ActivateAgent(context.Background(), &agentpb.ActivateAgentRequest{
Meta: secondMeta,
Binding: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1", ExpectedBootId: "boot-2", DispatcherEpoch: "epoch-2", SessionGeneration: 2},
ActivationOperationId: "activate-2",
})
require.NoError(t, err)
err = server.authorize(context.Background(), testMeta("old", "read-old", 1))
require.Error(t, err)
require.Equal(t, codes.Aborted, status.Code(err))
fresh := testMeta("fresh", "read-fresh", 2)
fresh.BootId = "boot-2"
fresh.DispatcherEpoch = "epoch-2"
require.NoError(t, server.authorize(context.Background(), fresh))
}
func TestGeneratedUnaryServiceWiring(t *testing.T) {
server := NewServer(ServerOptions{Now: func() time.Time { return time.Unix(100, 0) }})
listener := bufconn.Listen(1024 * 1024)
grpcServer := grpc.NewServer()
agentpb.RegisterAgentControlServiceServer(grpcServer, server)
go func() { _ = grpcServer.Serve(listener) }()
defer grpcServer.Stop()
conn, err := grpc.NewClient("passthrough:///bufnet", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { return listener.Dial() }), grpc.WithTransportCredentials(insecure.NewCredentials()))
require.NoError(t, err)
defer conn.Close()
client := agentpb.NewAgentControlServiceClient(conn)
meta := testMeta("activate-rpc", "", 0)
response, err := client.ActivateAgent(context.Background(), &agentpb.ActivateAgentRequest{Meta: meta, Binding: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1", ExpectedBootId: "boot-1", DispatcherEpoch: "epoch-1", SessionGeneration: 1}, ActivationOperationId: "activate-rpc"})
require.NoError(t, err)
require.Equal(t, agentpb.ActivationState_ACTIVATION_STATE_ACTIVE, response.State)
}
func activatedServer(now time.Time, t *testing.T) *Server {
t.Helper()
server := NewServer(ServerOptions{Now: func() time.Time { return now }})
meta := testMeta("activate", "", 0)
_, err := server.ActivateAgent(context.Background(), &agentpb.ActivateAgentRequest{Meta: meta, Binding: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1", ExpectedBootId: "boot-1", DispatcherEpoch: "epoch-1", SessionGeneration: 1}, ActivationOperationId: "activate"})
require.NoError(t, err)
return server
}
type testAssertions struct{}
var require testAssertions
func (testAssertions) NoError(t *testing.T, err error, _ ...any) {
t.Helper()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func (testAssertions) Error(t *testing.T, err error, _ ...any) {
t.Helper()
if err == nil {
t.Fatal("expected error")
}
}
func (testAssertions) Equal(t *testing.T, expected, actual any, _ ...any) {
t.Helper()
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("expected %#v, got %#v", expected, actual)
}
}
func testMeta(operationID, idempotencyKey string, generation uint64) *agentpb.RequestMeta {
return &agentpb.RequestMeta{ProtocolVersion: "agent.v1", RequestId: operationID + "-request", TraceId: "trace-1", OperationId: operationID, IdempotencyKey: idempotencyKey, DispatcherEpoch: "epoch-1", AgentId: "agent-1", CellId: "cell-1", BootId: "boot-1", SessionGeneration: generation}
}