mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-07 00:37:36 +00:00
201 lines
5.5 KiB
Go
201 lines
5.5 KiB
Go
package licensing
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
|
agentsdocker "github.com/rcourtman/pulse-go-rewrite/pkg/agents/docker"
|
|
agentshost "github.com/rcourtman/pulse-go-rewrite/pkg/agents/host"
|
|
agentsk8s "github.com/rcourtman/pulse-go-rewrite/pkg/agents/kubernetes"
|
|
)
|
|
|
|
const MaxMonitoredSystemsLicenseGateKey = "max_monitored_systems"
|
|
|
|
func ExceedsMonitoredSystemLimit(current, additions, limit int) bool {
|
|
if limit <= 0 || additions <= 0 {
|
|
return false
|
|
}
|
|
return current+additions > limit
|
|
}
|
|
|
|
func MonitoredSystemLimitExceededMessage(current, limit int) string {
|
|
return fmt.Sprintf("Monitored system limit reached (%d/%d). Remove a monitored system or upgrade your plan.", current, limit)
|
|
}
|
|
|
|
// InstalledUnifiedAgentCount returns the number of installed Pulse Unified
|
|
// Agents. This is inventory-only metadata and is not the commercial counted
|
|
// unit; monitored-system enforcement is derived from canonical unified
|
|
// resources instead.
|
|
func InstalledUnifiedAgentCount(state models.StateSnapshot) int {
|
|
return len(state.Hosts)
|
|
}
|
|
|
|
func CanonicalizeMonitoredSystemLimitKey(key string) string {
|
|
normalized := strings.TrimSpace(key)
|
|
switch normalized {
|
|
case "", MaxMonitoredSystemsLicenseGateKey:
|
|
return MaxMonitoredSystemsLicenseGateKey
|
|
}
|
|
if canonical, ok := canonicalizeLegacyV5MonitoredSystemLimitKey(normalized); ok {
|
|
return canonical
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
func NormalizeMonitoredSystemLimits(limits map[string]int64) map[string]int64 {
|
|
if limits == nil {
|
|
return nil
|
|
}
|
|
normalized := cloneInt64Map(limits)
|
|
if value, ok := MonitoredSystemLimitValue(normalized); ok {
|
|
normalized[MaxMonitoredSystemsLicenseGateKey] = value
|
|
}
|
|
for _, key := range legacyV5MonitoredSystemLimitAliasKeys {
|
|
delete(normalized, key)
|
|
}
|
|
return normalized
|
|
}
|
|
|
|
func MonitoredSystemLimitValue(limits map[string]int64) (int64, bool) {
|
|
if limits == nil {
|
|
return 0, false
|
|
}
|
|
for _, key := range []string{MaxMonitoredSystemsLicenseGateKey} {
|
|
if value, ok := limits[key]; ok {
|
|
return value, true
|
|
}
|
|
}
|
|
for _, key := range legacyV5MonitoredSystemLimitAliasKeys {
|
|
if value, ok := limits[key]; ok {
|
|
return value, true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func HostReportTargetsExistingHost(
|
|
snapshot models.StateSnapshot,
|
|
report agentshost.Report,
|
|
tokenID string,
|
|
) bool {
|
|
return HostReportTargetsExistingHosts(snapshot.Hosts, report, tokenID)
|
|
}
|
|
|
|
func HostReportTargetsExistingHosts(
|
|
hosts []models.Host,
|
|
report agentshost.Report,
|
|
tokenID string,
|
|
) bool {
|
|
hostname := strings.TrimSpace(report.Host.Hostname)
|
|
tokenID = strings.TrimSpace(tokenID)
|
|
|
|
candidates := CollectNonEmptyStrings(
|
|
report.Host.ID,
|
|
report.Host.MachineID,
|
|
report.Agent.ID,
|
|
)
|
|
|
|
for _, existing := range hosts {
|
|
for _, candidate := range candidates {
|
|
if existing.ID == candidate || existing.MachineID == candidate {
|
|
return true
|
|
}
|
|
}
|
|
|
|
if hostname != "" && strings.EqualFold(existing.Hostname, hostname) {
|
|
// Token-bound identity takes precedence when token is present.
|
|
if tokenID == "" || (existing.TokenID != "" && existing.TokenID == tokenID) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func DockerReportTargetsExistingHost(snapshot models.StateSnapshot, report agentsdocker.Report, tokenID string) bool {
|
|
hostname := strings.TrimSpace(report.Host.Hostname)
|
|
agentKey := strings.TrimSpace(report.AgentKey())
|
|
tokenID = strings.TrimSpace(tokenID)
|
|
|
|
for _, existing := range snapshot.DockerHosts {
|
|
if agentKey != "" && (existing.ID == agentKey || existing.AgentID == agentKey || existing.MachineID == agentKey) {
|
|
return true
|
|
}
|
|
|
|
if hostname != "" && strings.EqualFold(existing.Hostname, hostname) {
|
|
if tokenID == "" || (existing.TokenID != "" && existing.TokenID == tokenID) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func KubernetesReportTargetsExistingCluster(snapshot models.StateSnapshot, report agentsk8s.Report, tokenID string) bool {
|
|
identifier := KubernetesReportIdentifier(report)
|
|
agentID := strings.TrimSpace(report.Agent.ID)
|
|
clusterName := strings.TrimSpace(report.Cluster.Name)
|
|
tokenID = strings.TrimSpace(tokenID)
|
|
|
|
for _, existing := range snapshot.KubernetesClusters {
|
|
if identifier != "" && existing.ID == identifier {
|
|
return true
|
|
}
|
|
if agentID != "" && existing.AgentID == agentID {
|
|
return true
|
|
}
|
|
|
|
if tokenID != "" && existing.TokenID == tokenID {
|
|
if identifier == "" || existing.ID == identifier {
|
|
return true
|
|
}
|
|
if clusterName != "" && strings.EqualFold(existing.Name, clusterName) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
if tokenID == "" && identifier == "" && clusterName != "" && strings.EqualFold(existing.Name, clusterName) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func KubernetesReportIdentifier(report agentsk8s.Report) string {
|
|
if v := strings.TrimSpace(report.Cluster.ID); v != "" {
|
|
return v
|
|
}
|
|
if v := strings.TrimSpace(report.Agent.ID); v != "" {
|
|
return v
|
|
}
|
|
stableKey := strings.TrimSpace(report.Cluster.Server) + "|" + strings.TrimSpace(report.Cluster.Context) + "|" + strings.TrimSpace(report.Cluster.Name)
|
|
stableKey = strings.TrimSpace(stableKey)
|
|
if stableKey == "||" || stableKey == "" {
|
|
return ""
|
|
}
|
|
sum := sha256.Sum256([]byte(stableKey))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func CollectNonEmptyStrings(values ...string) []string {
|
|
out := make([]string, 0, len(values))
|
|
seen := make(map[string]struct{}, len(values))
|
|
for _, value := range values {
|
|
trimmed := strings.TrimSpace(value)
|
|
if trimmed == "" {
|
|
continue
|
|
}
|
|
if _, exists := seen[trimmed]; exists {
|
|
continue
|
|
}
|
|
seen[trimmed] = struct{}{}
|
|
out = append(out, trimmed)
|
|
}
|
|
return out
|
|
}
|