268 lines
9.2 KiB
Go
268 lines
9.2 KiB
Go
package webingress
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/example/remote-access-platform/agents/rap-node-agent/internal/mesh"
|
|
)
|
|
|
|
func TestMeshEnvelopeSenderSendsSignedEnvelopeOverReliableFabricRuntime(t *testing.T) {
|
|
runtime := &recordingReliableRuntime{
|
|
result: mesh.FabricChannelRuntimeResult{
|
|
Channel: mesh.FabricChannel{RouteID: "route-fast", TargetNode: "node-runtime"},
|
|
BytesSent: 123,
|
|
FramesSent: 1,
|
|
AcksReceived: 1,
|
|
},
|
|
}
|
|
sender := MeshEnvelopeSender{
|
|
Runtime: runtime,
|
|
RouteSet: testWebIngressRouteSet(),
|
|
ClusterID: "cluster-1",
|
|
SourceNodeID: "node-ingress",
|
|
TargetKind: mesh.FabricChannelTargetPool,
|
|
TargetID: "pool-admin-runtime",
|
|
ChannelID: "channel-web-1",
|
|
Now: fixedEnvelopeNow,
|
|
}
|
|
envelope := SignedFabricServiceChannelEnvelope{
|
|
SchemaVersion: "rap.web_ingress.signed_fabric_service_channel_envelope.v1",
|
|
Envelope: FabricServiceChannelEnvelope{
|
|
SchemaVersion: FabricServiceChannelEnvelopeSchema,
|
|
Scope: "platform",
|
|
ServiceClass: "platform_admin",
|
|
},
|
|
Signature: FabricEnvelopeSignature{KeyID: "node-key", Alg: "ed25519", Signature: "sig"},
|
|
}
|
|
|
|
response, err := sender.Send(context.Background(), envelope)
|
|
if err != nil {
|
|
t.Fatalf("send: %v", err)
|
|
}
|
|
if response.StatusCode != http.StatusAccepted || response.Headers.Get("Content-Type") != "application/json" {
|
|
t.Fatalf("response = %+v", response)
|
|
}
|
|
if runtime.spec.ChannelID != "channel-web-1" ||
|
|
runtime.spec.ClusterID != "cluster-1" ||
|
|
runtime.spec.SourceNodeID != "node-ingress" ||
|
|
runtime.spec.TargetID != "pool-admin-runtime" ||
|
|
runtime.spec.TargetKind != mesh.FabricChannelTargetPool ||
|
|
runtime.spec.TrafficClass != "control" ||
|
|
runtime.spec.StickyKey != "platform:platform_admin" {
|
|
t.Fatalf("spec = %+v", runtime.spec)
|
|
}
|
|
if runtime.routeSet.TargetID != "pool-admin-runtime" || len(runtime.payloads) != 1 {
|
|
t.Fatalf("route/payload = %+v payloads=%d", runtime.routeSet, len(runtime.payloads))
|
|
}
|
|
var delivered SignedFabricServiceChannelEnvelope
|
|
if err := json.Unmarshal(runtime.payloads[0], &delivered); err != nil {
|
|
t.Fatalf("decode delivered envelope: %v", err)
|
|
}
|
|
if delivered.Signature.Signature != "sig" || delivered.Envelope.ServiceClass != "platform_admin" {
|
|
t.Fatalf("delivered = %+v", delivered)
|
|
}
|
|
var body MeshEnvelopeDeliveryResponse
|
|
if err := json.Unmarshal(response.Body, &body); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if body.SchemaVersion != "rap.web_ingress.mesh_envelope_delivery_response.v1" ||
|
|
body.Status != "accepted" ||
|
|
body.RouteID != "route-fast" ||
|
|
body.AcksReceived != 1 {
|
|
t.Fatalf("body = %+v", body)
|
|
}
|
|
}
|
|
|
|
func TestMeshEnvelopeSenderReturnsRuntimeHTTPResponse(t *testing.T) {
|
|
runtime := &recordingRequestResponseRuntime{
|
|
result: mesh.FabricChannelRequestResponseResult{
|
|
FabricChannelRuntimeResult: mesh.FabricChannelRuntimeResult{
|
|
Channel: mesh.FabricChannel{RouteID: "route-runtime", TargetNode: "node-runtime"},
|
|
BytesSent: 123,
|
|
BytesRecv: 16,
|
|
FramesSent: 1,
|
|
FramesRecv: 1,
|
|
AcksReceived: 1,
|
|
},
|
|
ResponsePayload: []byte(`{"payload":{"schema_version":"rap.web_ingress.fabric_runtime_response.v1","status_code":201,"headers":{"X-RAP-Runtime":["ok"],"Set-Cookie":["blocked"]},"body_b64":"eyJvayI6dHJ1ZX0="}}`),
|
|
},
|
|
}
|
|
sender := MeshEnvelopeSender{
|
|
ResponseRuntime: runtime,
|
|
RouteSet: testWebIngressRouteSet(),
|
|
ClusterID: "cluster-1",
|
|
SourceNodeID: "node-ingress",
|
|
TargetKind: mesh.FabricChannelTargetPool,
|
|
TargetID: "pool-admin-runtime",
|
|
ChannelID: "channel-web-1",
|
|
Now: fixedEnvelopeNow,
|
|
}
|
|
|
|
response, err := sender.Send(context.Background(), SignedFabricServiceChannelEnvelope{
|
|
SchemaVersion: "rap.web_ingress.signed_fabric_service_channel_envelope.v1",
|
|
Envelope: FabricServiceChannelEnvelope{SchemaVersion: FabricServiceChannelEnvelopeSchema, Scope: "platform", ServiceClass: "platform_admin"},
|
|
Signature: FabricEnvelopeSignature{KeyID: "node-key", Alg: "ed25519", Signature: "sig"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("send: %v", err)
|
|
}
|
|
if response.StatusCode != http.StatusCreated || response.Headers.Get("X-RAP-Runtime") != "ok" || response.Headers.Get("Set-Cookie") != "" || string(response.Body) != `{"ok":true}` {
|
|
t.Fatalf("response = %+v body=%s", response, string(response.Body))
|
|
}
|
|
if runtime.spec.ChannelID != "channel-web-1" || len(runtime.payload) == 0 {
|
|
t.Fatalf("runtime spec=%+v payload=%s", runtime.spec, string(runtime.payload))
|
|
}
|
|
}
|
|
|
|
func TestMeshEnvelopeSenderReportsWrappedRuntimeError(t *testing.T) {
|
|
sender := MeshEnvelopeSender{
|
|
ResponseRuntime: &recordingRequestResponseRuntime{
|
|
result: mesh.FabricChannelRequestResponseResult{ResponsePayload: []byte(`{"error":"runtime unavailable"}`)},
|
|
},
|
|
RouteSet: testWebIngressRouteSet(),
|
|
ClusterID: "cluster-1",
|
|
SourceNodeID: "node-ingress",
|
|
TargetID: "pool-admin-runtime",
|
|
ChannelID: "channel-web-1",
|
|
}
|
|
|
|
_, err := sender.Send(context.Background(), SignedFabricServiceChannelEnvelope{
|
|
Envelope: FabricServiceChannelEnvelope{Scope: "platform", ServiceClass: "platform_admin"},
|
|
})
|
|
if !errors.Is(err, ErrMeshEnvelopeRuntimeRequired) {
|
|
t.Fatalf("err = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMeshEnvelopeSenderFallsBackToDeliveryAckForNonHTTPRuntimePayload(t *testing.T) {
|
|
runtime := &recordingRequestResponseRuntime{
|
|
result: mesh.FabricChannelRequestResponseResult{
|
|
FabricChannelRuntimeResult: mesh.FabricChannelRuntimeResult{
|
|
Channel: mesh.FabricChannel{RouteID: "route-runtime", TargetNode: "node-runtime"},
|
|
BytesSent: 123,
|
|
FramesSent: 1,
|
|
AcksReceived: 1,
|
|
},
|
|
ResponsePayload: []byte(`{"not":"http"}`),
|
|
},
|
|
}
|
|
sender := MeshEnvelopeSender{
|
|
ResponseRuntime: runtime,
|
|
RouteSet: testWebIngressRouteSet(),
|
|
ClusterID: "cluster-1",
|
|
SourceNodeID: "node-ingress",
|
|
TargetID: "pool-admin-runtime",
|
|
ChannelID: "channel-web-1",
|
|
}
|
|
|
|
response, err := sender.Send(context.Background(), SignedFabricServiceChannelEnvelope{
|
|
Envelope: FabricServiceChannelEnvelope{Scope: "platform", ServiceClass: "platform_admin"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("send: %v", err)
|
|
}
|
|
if response.StatusCode != http.StatusAccepted {
|
|
t.Fatalf("response = %+v", response)
|
|
}
|
|
var body MeshEnvelopeDeliveryResponse
|
|
if err := json.Unmarshal(response.Body, &body); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if body.Status != "accepted" || body.RouteID != "route-runtime" {
|
|
t.Fatalf("body = %+v", body)
|
|
}
|
|
}
|
|
|
|
func TestMeshEnvelopeSenderReportsRuntimeRouteAndIdentityErrors(t *testing.T) {
|
|
_, err := (MeshEnvelopeSender{}).Send(context.Background(), SignedFabricServiceChannelEnvelope{})
|
|
if !errors.Is(err, ErrMeshEnvelopeRuntimeRequired) {
|
|
t.Fatalf("runtime error = %v", err)
|
|
}
|
|
|
|
_, err = (MeshEnvelopeSender{
|
|
Runtime: &recordingReliableRuntime{},
|
|
ClusterID: "cluster-1",
|
|
SourceNodeID: "node-ingress",
|
|
TargetID: "pool-admin-runtime",
|
|
}).Send(context.Background(), SignedFabricServiceChannelEnvelope{})
|
|
if !errors.Is(err, ErrMeshEnvelopeRouteRequired) {
|
|
t.Fatalf("route error = %v", err)
|
|
}
|
|
|
|
_, err = (MeshEnvelopeSender{
|
|
Runtime: &recordingReliableRuntime{},
|
|
RouteSet: testWebIngressRouteSet(),
|
|
}).Send(context.Background(), SignedFabricServiceChannelEnvelope{})
|
|
if !errors.Is(err, ErrMeshEnvelopeIdentityInvalid) {
|
|
t.Fatalf("identity error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMeshEnvelopeSenderPropagatesReliableRuntimeFailure(t *testing.T) {
|
|
sendErr := errors.New("send failed")
|
|
_, err := (MeshEnvelopeSender{
|
|
Runtime: &recordingReliableRuntime{err: sendErr},
|
|
RouteSet: testWebIngressRouteSet(),
|
|
ClusterID: "cluster-1",
|
|
SourceNodeID: "node-ingress",
|
|
TargetID: "pool-admin-runtime",
|
|
}).Send(context.Background(), SignedFabricServiceChannelEnvelope{})
|
|
if !errors.Is(err, sendErr) {
|
|
t.Fatalf("send error = %v", err)
|
|
}
|
|
}
|
|
|
|
type recordingReliableRuntime struct {
|
|
spec mesh.FabricChannelSpec
|
|
routeSet mesh.FabricRouteSet
|
|
payloads [][]byte
|
|
result mesh.FabricChannelRuntimeResult
|
|
err error
|
|
}
|
|
|
|
type recordingRequestResponseRuntime struct {
|
|
spec mesh.FabricChannelSpec
|
|
routeSet mesh.FabricRouteSet
|
|
payload []byte
|
|
result mesh.FabricChannelRequestResponseResult
|
|
err error
|
|
}
|
|
|
|
func (r *recordingRequestResponseRuntime) SendRequestResponse(_ context.Context, spec mesh.FabricChannelSpec, routeSet mesh.FabricRouteSet, payload []byte) (mesh.FabricChannelRequestResponseResult, error) {
|
|
r.spec = spec
|
|
r.routeSet = routeSet
|
|
r.payload = payload
|
|
if r.err != nil {
|
|
return mesh.FabricChannelRequestResponseResult{}, r.err
|
|
}
|
|
return r.result, nil
|
|
}
|
|
|
|
func (r *recordingReliableRuntime) SendReliable(_ context.Context, spec mesh.FabricChannelSpec, routeSet mesh.FabricRouteSet, payloads [][]byte) (mesh.FabricChannelRuntimeResult, error) {
|
|
r.spec = spec
|
|
r.routeSet = routeSet
|
|
r.payloads = payloads
|
|
if r.err != nil {
|
|
return mesh.FabricChannelRuntimeResult{}, r.err
|
|
}
|
|
return r.result, nil
|
|
}
|
|
|
|
func testWebIngressRouteSet() mesh.FabricRouteSet {
|
|
return mesh.FabricRouteSet{
|
|
Primary: mesh.FabricRoute{
|
|
RouteID: "route-fast",
|
|
ClusterID: "cluster-1",
|
|
SourceNodeID: "node-ingress",
|
|
DestinationNodeID: "node-runtime",
|
|
PoolID: "pool-admin-runtime",
|
|
Healthy: true,
|
|
Capacity: 100,
|
|
},
|
|
}
|
|
}
|