mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 08:16:39 +00:00
[management] Add custom domain counts and service metrics to self-hosted metrics (#5414)
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/hashicorp/go-version"
|
"github.com/hashicorp/go-version"
|
||||||
"github.com/netbirdio/netbird/idp/dex"
|
"github.com/netbirdio/netbird/idp/dex"
|
||||||
|
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/types"
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
@@ -51,6 +52,7 @@ type properties map[string]interface{}
|
|||||||
type DataSource interface {
|
type DataSource interface {
|
||||||
GetAllAccounts(ctx context.Context) []*types.Account
|
GetAllAccounts(ctx context.Context) []*types.Account
|
||||||
GetStoreEngine() types.Engine
|
GetStoreEngine() types.Engine
|
||||||
|
GetCustomDomainsCounts(ctx context.Context) (total int64, validated int64, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnManager peer connection manager that holds state for current active connections
|
// ConnManager peer connection manager that holds state for current active connections
|
||||||
@@ -211,6 +213,16 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
|
|||||||
localUsers int
|
localUsers int
|
||||||
idpUsers int
|
idpUsers int
|
||||||
embeddedIdpTypes map[string]int
|
embeddedIdpTypes map[string]int
|
||||||
|
services int
|
||||||
|
servicesEnabled int
|
||||||
|
servicesTargets int
|
||||||
|
servicesStatusActive int
|
||||||
|
servicesStatusPending int
|
||||||
|
servicesStatusError int
|
||||||
|
servicesTargetType map[string]int
|
||||||
|
servicesAuthPassword int
|
||||||
|
servicesAuthPin int
|
||||||
|
servicesAuthOIDC int
|
||||||
)
|
)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
metricsProperties := make(properties)
|
metricsProperties := make(properties)
|
||||||
@@ -220,10 +232,13 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
|
|||||||
rulesDirection = make(map[string]int)
|
rulesDirection = make(map[string]int)
|
||||||
activeUsersLastDay = make(map[string]struct{})
|
activeUsersLastDay = make(map[string]struct{})
|
||||||
embeddedIdpTypes = make(map[string]int)
|
embeddedIdpTypes = make(map[string]int)
|
||||||
|
servicesTargetType = make(map[string]int)
|
||||||
uptime = time.Since(w.startupTime).Seconds()
|
uptime = time.Since(w.startupTime).Seconds()
|
||||||
connections := w.connManager.GetAllConnectedPeers()
|
connections := w.connManager.GetAllConnectedPeers()
|
||||||
version = nbversion.NetbirdVersion()
|
version = nbversion.NetbirdVersion()
|
||||||
|
|
||||||
|
customDomains, customDomainsValidated, _ := w.dataSource.GetCustomDomainsCounts(ctx)
|
||||||
|
|
||||||
for _, account := range w.dataSource.GetAllAccounts(ctx) {
|
for _, account := range w.dataSource.GetAllAccounts(ctx) {
|
||||||
accounts++
|
accounts++
|
||||||
|
|
||||||
@@ -335,6 +350,37 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
|
|||||||
peerActiveVersions = append(peerActiveVersions, peer.Meta.WtVersion)
|
peerActiveVersions = append(peerActiveVersions, peer.Meta.WtVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, service := range account.Services {
|
||||||
|
services++
|
||||||
|
if service.Enabled {
|
||||||
|
servicesEnabled++
|
||||||
|
}
|
||||||
|
servicesTargets += len(service.Targets)
|
||||||
|
|
||||||
|
switch reverseproxy.ProxyStatus(service.Meta.Status) {
|
||||||
|
case reverseproxy.StatusActive:
|
||||||
|
servicesStatusActive++
|
||||||
|
case reverseproxy.StatusPending:
|
||||||
|
servicesStatusPending++
|
||||||
|
case reverseproxy.StatusError, reverseproxy.StatusCertificateFailed, reverseproxy.StatusTunnelNotCreated:
|
||||||
|
servicesStatusError++
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, target := range service.Targets {
|
||||||
|
servicesTargetType[target.TargetType]++
|
||||||
|
}
|
||||||
|
|
||||||
|
if service.Auth.PasswordAuth != nil && service.Auth.PasswordAuth.Enabled {
|
||||||
|
servicesAuthPassword++
|
||||||
|
}
|
||||||
|
if service.Auth.PinAuth != nil && service.Auth.PinAuth.Enabled {
|
||||||
|
servicesAuthPin++
|
||||||
|
}
|
||||||
|
if service.Auth.BearerAuth != nil && service.Auth.BearerAuth.Enabled {
|
||||||
|
servicesAuthOIDC++
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
minActivePeerVersion, maxActivePeerVersion := getMinMaxVersion(peerActiveVersions)
|
minActivePeerVersion, maxActivePeerVersion := getMinMaxVersion(peerActiveVersions)
|
||||||
@@ -375,6 +421,22 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
|
|||||||
metricsProperties["idp_users_count"] = idpUsers
|
metricsProperties["idp_users_count"] = idpUsers
|
||||||
metricsProperties["embedded_idp_count"] = len(embeddedIdpTypes)
|
metricsProperties["embedded_idp_count"] = len(embeddedIdpTypes)
|
||||||
|
|
||||||
|
metricsProperties["services"] = services
|
||||||
|
metricsProperties["services_enabled"] = servicesEnabled
|
||||||
|
metricsProperties["services_targets"] = servicesTargets
|
||||||
|
metricsProperties["services_status_active"] = servicesStatusActive
|
||||||
|
metricsProperties["services_status_pending"] = servicesStatusPending
|
||||||
|
metricsProperties["services_status_error"] = servicesStatusError
|
||||||
|
metricsProperties["services_auth_password"] = servicesAuthPassword
|
||||||
|
metricsProperties["services_auth_pin"] = servicesAuthPin
|
||||||
|
metricsProperties["services_auth_oidc"] = servicesAuthOIDC
|
||||||
|
metricsProperties["custom_domains"] = customDomains
|
||||||
|
metricsProperties["custom_domains_validated"] = customDomainsValidated
|
||||||
|
|
||||||
|
for targetType, count := range servicesTargetType {
|
||||||
|
metricsProperties["services_target_type_"+targetType] = count
|
||||||
|
}
|
||||||
|
|
||||||
for idpType, count := range embeddedIdpTypes {
|
for idpType, count := range embeddedIdpTypes {
|
||||||
metricsProperties["embedded_idp_users_"+idpType] = count
|
metricsProperties["embedded_idp_users_"+idpType] = count
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
"github.com/netbirdio/netbird/idp/dex"
|
"github.com/netbirdio/netbird/idp/dex"
|
||||||
|
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
|
||||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||||
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
||||||
@@ -115,6 +116,31 @@ func (mockDatasource) GetAllAccounts(_ context.Context) []*types.Account {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Services: []*reverseproxy.Service{
|
||||||
|
{
|
||||||
|
ID: "svc1",
|
||||||
|
Enabled: true,
|
||||||
|
Targets: []*reverseproxy.Target{
|
||||||
|
{TargetType: "peer"},
|
||||||
|
{TargetType: "host"},
|
||||||
|
},
|
||||||
|
Auth: reverseproxy.AuthConfig{
|
||||||
|
PasswordAuth: &reverseproxy.PasswordAuthConfig{Enabled: true},
|
||||||
|
},
|
||||||
|
Meta: reverseproxy.ServiceMeta{Status: string(reverseproxy.StatusActive)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "svc2",
|
||||||
|
Enabled: false,
|
||||||
|
Targets: []*reverseproxy.Target{
|
||||||
|
{TargetType: "domain"},
|
||||||
|
},
|
||||||
|
Auth: reverseproxy.AuthConfig{
|
||||||
|
BearerAuth: &reverseproxy.BearerAuthConfig{Enabled: true},
|
||||||
|
},
|
||||||
|
Meta: reverseproxy.ServiceMeta{Status: string(reverseproxy.StatusPending)},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: "2",
|
Id: "2",
|
||||||
@@ -215,6 +241,11 @@ func (mockDatasource) GetStoreEngine() types.Engine {
|
|||||||
return types.FileStoreEngine
|
return types.FileStoreEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCustomDomainsCounts returns test custom domain counts.
|
||||||
|
func (mockDatasource) GetCustomDomainsCounts(_ context.Context) (int64, int64, error) {
|
||||||
|
return 3, 2, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TestGenerateProperties tests and validate the properties generation by using the mockDatasource for the Worker.generateProperties
|
// TestGenerateProperties tests and validate the properties generation by using the mockDatasource for the Worker.generateProperties
|
||||||
func TestGenerateProperties(t *testing.T) {
|
func TestGenerateProperties(t *testing.T) {
|
||||||
ds := mockDatasource{}
|
ds := mockDatasource{}
|
||||||
@@ -347,6 +378,49 @@ func TestGenerateProperties(t *testing.T) {
|
|||||||
if properties["embedded_idp_count"] != 1 {
|
if properties["embedded_idp_count"] != 1 {
|
||||||
t.Errorf("expected 1 embedded_idp_count, got %v", properties["embedded_idp_count"])
|
t.Errorf("expected 1 embedded_idp_count, got %v", properties["embedded_idp_count"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if properties["services"] != 2 {
|
||||||
|
t.Errorf("expected 2 services, got %v", properties["services"])
|
||||||
|
}
|
||||||
|
if properties["services_enabled"] != 1 {
|
||||||
|
t.Errorf("expected 1 services_enabled, got %v", properties["services_enabled"])
|
||||||
|
}
|
||||||
|
if properties["services_targets"] != 3 {
|
||||||
|
t.Errorf("expected 3 services_targets, got %v", properties["services_targets"])
|
||||||
|
}
|
||||||
|
if properties["services_status_active"] != 1 {
|
||||||
|
t.Errorf("expected 1 services_status_active, got %v", properties["services_status_active"])
|
||||||
|
}
|
||||||
|
if properties["services_status_pending"] != 1 {
|
||||||
|
t.Errorf("expected 1 services_status_pending, got %v", properties["services_status_pending"])
|
||||||
|
}
|
||||||
|
if properties["services_status_error"] != 0 {
|
||||||
|
t.Errorf("expected 0 services_status_error, got %v", properties["services_status_error"])
|
||||||
|
}
|
||||||
|
if properties["services_target_type_peer"] != 1 {
|
||||||
|
t.Errorf("expected 1 services_target_type_peer, got %v", properties["services_target_type_peer"])
|
||||||
|
}
|
||||||
|
if properties["services_target_type_host"] != 1 {
|
||||||
|
t.Errorf("expected 1 services_target_type_host, got %v", properties["services_target_type_host"])
|
||||||
|
}
|
||||||
|
if properties["services_target_type_domain"] != 1 {
|
||||||
|
t.Errorf("expected 1 services_target_type_domain, got %v", properties["services_target_type_domain"])
|
||||||
|
}
|
||||||
|
if properties["services_auth_password"] != 1 {
|
||||||
|
t.Errorf("expected 1 services_auth_password, got %v", properties["services_auth_password"])
|
||||||
|
}
|
||||||
|
if properties["services_auth_oidc"] != 1 {
|
||||||
|
t.Errorf("expected 1 services_auth_oidc, got %v", properties["services_auth_oidc"])
|
||||||
|
}
|
||||||
|
if properties["services_auth_pin"] != 0 {
|
||||||
|
t.Errorf("expected 0 services_auth_pin, got %v", properties["services_auth_pin"])
|
||||||
|
}
|
||||||
|
if properties["custom_domains"] != int64(3) {
|
||||||
|
t.Errorf("expected 3 custom_domains, got %v", properties["custom_domains"])
|
||||||
|
}
|
||||||
|
if properties["custom_domains_validated"] != int64(2) {
|
||||||
|
t.Errorf("expected 2 custom_domains_validated, got %v", properties["custom_domains_validated"])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractIdpType(t *testing.T) {
|
func TestExtractIdpType(t *testing.T) {
|
||||||
|
|||||||
@@ -269,3 +269,8 @@ func (s *FileStore) GetStoreEngine() types.Engine {
|
|||||||
func (s *FileStore) SetFieldEncrypt(_ *crypt.FieldEncrypt) {
|
func (s *FileStore) SetFieldEncrypt(_ *crypt.FieldEncrypt) {
|
||||||
// no-op: FileStore stores data in plaintext JSON; encryption is not supported
|
// no-op: FileStore stores data in plaintext JSON; encryption is not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCustomDomainsCounts is a no-op for FileStore as it doesn't support custom domains.
|
||||||
|
func (s *FileStore) GetCustomDomainsCounts(_ context.Context) (int64, int64, error) {
|
||||||
|
return 0, 0, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1007,6 +1007,18 @@ func (s *SqlStore) GetAccountsCounter(ctx context.Context) (int64, error) {
|
|||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCustomDomainsCounts returns the total and validated custom domain counts.
|
||||||
|
func (s *SqlStore) GetCustomDomainsCounts(ctx context.Context) (int64, int64, error) {
|
||||||
|
var total, validated int64
|
||||||
|
if err := s.db.WithContext(ctx).Model(&domain.Domain{}).Count(&total).Error; err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
if err := s.db.WithContext(ctx).Model(&domain.Domain{}).Where("validated = ?", true).Count(&validated).Error; err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
return total, validated, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SqlStore) GetAllAccounts(ctx context.Context) (all []*types.Account) {
|
func (s *SqlStore) GetAllAccounts(ctx context.Context) (all []*types.Account) {
|
||||||
var accounts []types.Account
|
var accounts []types.Account
|
||||||
result := s.db.Find(&accounts)
|
result := s.db.Find(&accounts)
|
||||||
|
|||||||
@@ -272,6 +272,9 @@ type Store interface {
|
|||||||
GetAccountAccessLogs(ctx context.Context, lockStrength LockingStrength, accountID string, filter accesslogs.AccessLogFilter) ([]*accesslogs.AccessLogEntry, int64, error)
|
GetAccountAccessLogs(ctx context.Context, lockStrength LockingStrength, accountID string, filter accesslogs.AccessLogFilter) ([]*accesslogs.AccessLogEntry, int64, error)
|
||||||
DeleteOldAccessLogs(ctx context.Context, olderThan time.Time) (int64, error)
|
DeleteOldAccessLogs(ctx context.Context, olderThan time.Time) (int64, error)
|
||||||
GetServiceTargetByTargetID(ctx context.Context, lockStrength LockingStrength, accountID string, targetID string) (*reverseproxy.Target, error)
|
GetServiceTargetByTargetID(ctx context.Context, lockStrength LockingStrength, accountID string, targetID string) (*reverseproxy.Target, error)
|
||||||
|
|
||||||
|
// GetCustomDomainsCounts returns the total and validated custom domain counts.
|
||||||
|
GetCustomDomainsCounts(ctx context.Context) (total int64, validated int64, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -1872,6 +1872,22 @@ func (mr *MockStoreMockRecorder) GetServiceTargetByTargetID(ctx, lockStrength, a
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceTargetByTargetID", reflect.TypeOf((*MockStore)(nil).GetServiceTargetByTargetID), ctx, lockStrength, accountID, targetID)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceTargetByTargetID", reflect.TypeOf((*MockStore)(nil).GetServiceTargetByTargetID), ctx, lockStrength, accountID, targetID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCustomDomainsCounts mocks base method.
|
||||||
|
func (m *MockStore) GetCustomDomainsCounts(ctx context.Context) (int64, int64, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetCustomDomainsCounts", ctx)
|
||||||
|
ret0, _ := ret[0].(int64)
|
||||||
|
ret1, _ := ret[1].(int64)
|
||||||
|
ret2, _ := ret[2].(error)
|
||||||
|
return ret0, ret1, ret2
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCustomDomainsCounts indicates an expected call of GetCustomDomainsCounts.
|
||||||
|
func (mr *MockStoreMockRecorder) GetCustomDomainsCounts(ctx interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCustomDomainsCounts", reflect.TypeOf((*MockStore)(nil).GetCustomDomainsCounts), ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// GetServices mocks base method.
|
// GetServices mocks base method.
|
||||||
func (m *MockStore) GetServices(ctx context.Context, lockStrength LockingStrength) ([]*reverseproxy.Service, error) {
|
func (m *MockStore) GetServices(ctx context.Context, lockStrength LockingStrength) ([]*reverseproxy.Service, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
Reference in New Issue
Block a user