mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-14 18:59:08 +02:00
127 lines
5.1 KiB
Go
127 lines
5.1 KiB
Go
package manager
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.opentelemetry.io/otel/metric/noop"
|
|
|
|
domainmanager "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/domain/manager"
|
|
proxymanager "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/proxy/manager"
|
|
rpservice "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
|
|
"github.com/netbirdio/netbird/management/server/activity"
|
|
"github.com/netbirdio/netbird/management/server/mock_server"
|
|
"github.com/netbirdio/netbird/management/server/store"
|
|
"github.com/netbirdio/netbird/shared/management/status"
|
|
)
|
|
|
|
const validationTestCluster = "eu.proxy.test"
|
|
|
|
// withRealDomainManager swaps the stub cluster deriver for the real domain
|
|
// manager backed by the same store, so service creation is gated by the actual
|
|
// domain rows rather than by a test double that always agrees.
|
|
func withRealDomainManager(t *testing.T, mgr *Manager, testStore store.Store) {
|
|
t.Helper()
|
|
|
|
ctx := context.Background()
|
|
proxyMgr, err := proxymanager.NewManager(testStore, noop.NewMeterProvider().Meter(""))
|
|
require.NoError(t, err)
|
|
|
|
_, err = proxyMgr.Connect(ctx, "proxy-1", "session-1", validationTestCluster, "127.0.0.1", nil, nil)
|
|
require.NoError(t, err)
|
|
|
|
accountMgr := &mock_server.MockAccountManager{
|
|
StoreEventFunc: func(context.Context, string, string, string, activity.ActivityDescriber, map[string]any) {},
|
|
}
|
|
mgr.clusterDeriver = domainmanager.NewManager(testStore, proxyMgr, accountMgr)
|
|
}
|
|
|
|
func newTestService(domain string) *rpservice.Service {
|
|
return &rpservice.Service{
|
|
Name: "test-service",
|
|
Domain: domain,
|
|
Enabled: true,
|
|
Mode: rpservice.ModeHTTP,
|
|
Targets: []*rpservice.Target{{
|
|
Host: "10.0.0.1",
|
|
Port: 8080,
|
|
Protocol: "http",
|
|
TargetId: testPeerID,
|
|
TargetType: "peer",
|
|
Enabled: true,
|
|
}},
|
|
}
|
|
}
|
|
|
|
// A service must not bind to a domain the account has not validated, and
|
|
// nothing may be persisted for the attempt.
|
|
func TestCreateService_RefusesUnvalidatedDomain(t *testing.T) {
|
|
ctx := context.Background()
|
|
mgr, testStore := setupIntegrationTest(t)
|
|
withRealDomainManager(t, mgr, testStore)
|
|
|
|
_, err := testStore.CreateCustomDomain(ctx, testAccountID, "unproven.example.com", validationTestCluster, false)
|
|
require.NoError(t, err)
|
|
|
|
_, err = mgr.CreateService(ctx, testAccountID, testUserID, newTestService("unproven.example.com"))
|
|
require.Error(t, err, "an unvalidated domain must not bind a service")
|
|
assert.Contains(t, err.Error(), "not validated", "the API error should name the actual problem")
|
|
|
|
sErr, ok := status.FromError(err)
|
|
require.True(t, ok, "error should be a typed status error")
|
|
assert.Equal(t, status.PreconditionFailed, sErr.Type())
|
|
|
|
services, err := testStore.GetAccountServices(ctx, store.LockingStrengthNone, testAccountID)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, services, "no service row should be written for a refused domain")
|
|
}
|
|
|
|
// The negative control: a validated domain still binds a service and derives
|
|
// its cluster exactly as before.
|
|
func TestCreateService_ValidatedDomainBindsService(t *testing.T) {
|
|
ctx := context.Background()
|
|
mgr, testStore := setupIntegrationTest(t)
|
|
withRealDomainManager(t, mgr, testStore)
|
|
|
|
_, err := testStore.CreateCustomDomain(ctx, testAccountID, "proven.example.com", validationTestCluster, true)
|
|
require.NoError(t, err)
|
|
|
|
created, err := mgr.CreateService(ctx, testAccountID, testUserID, newTestService("app.proven.example.com"))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, validationTestCluster, created.ProxyCluster, "service should bind to the domain's target cluster")
|
|
|
|
services, err := testStore.GetAccountServices(ctx, store.LockingStrengthNone, testAccountID)
|
|
require.NoError(t, err)
|
|
require.Len(t, services, 1, "the service should be persisted")
|
|
assert.Equal(t, "app.proven.example.com", services[0].Domain)
|
|
}
|
|
|
|
// An update must not be a way around the creation gate: moving a live service
|
|
// onto an unvalidated domain has to fail rather than silently keep the old
|
|
// cluster and start serving the new hostname.
|
|
func TestUpdateService_RefusesMoveToUnvalidatedDomain(t *testing.T) {
|
|
ctx := context.Background()
|
|
mgr, testStore := setupIntegrationTest(t)
|
|
withRealDomainManager(t, mgr, testStore)
|
|
|
|
_, err := testStore.CreateCustomDomain(ctx, testAccountID, "proven.example.com", validationTestCluster, true)
|
|
require.NoError(t, err)
|
|
_, err = testStore.CreateCustomDomain(ctx, testAccountID, "unproven.example.com", validationTestCluster, false)
|
|
require.NoError(t, err)
|
|
|
|
created, err := mgr.CreateService(ctx, testAccountID, testUserID, newTestService("app.proven.example.com"))
|
|
require.NoError(t, err)
|
|
|
|
moved := *created
|
|
moved.Domain = "app.unproven.example.com"
|
|
_, err = mgr.UpdateService(ctx, testAccountID, testUserID, &moved)
|
|
require.Error(t, err, "moving to an unvalidated domain must fail")
|
|
assert.Contains(t, err.Error(), "not validated")
|
|
|
|
stored, err := testStore.GetServiceByID(ctx, store.LockingStrengthNone, testAccountID, created.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "app.proven.example.com", stored.Domain, "the service must keep its original domain")
|
|
}
|