@@ -3,19 +3,25 @@ package licensereporting
33import (
44 "context"
55 "encoding/json"
6+ "fmt"
67 "log/slog"
78 "os"
89 "path/filepath"
910 "time"
1011
1112 nl "github.com/nginxinc/kubernetes-ingress/internal/logger"
13+ "github.com/nginxinc/nginx-plus-go-client/v2/client"
1214
1315 clusterInfo "github.com/nginxinc/kubernetes-ingress/internal/common_cluster_info"
16+ api_v1 "k8s.io/api/core/v1"
1417 "k8s.io/apimachinery/pkg/types"
1518 "k8s.io/apimachinery/pkg/util/wait"
1619 "k8s.io/client-go/kubernetes"
20+ "k8s.io/client-go/tools/record"
1721)
1822
23+ const expiryThreshold = 30 * (time .Hour * 24 )
24+
1925var (
2026 reportingDir = "/etc/nginx/reporting"
2127 reportingFile = "tracking.info"
@@ -51,46 +57,91 @@ func writeLicenseInfo(l *slog.Logger, info *licenseInfo) {
5157
5258// LicenseReporter can start the license reporting process
5359type LicenseReporter struct {
54- config LicenseReporterConfig
60+ Config LicenseReporterConfig
5561}
5662
5763// LicenseReporterConfig contains the information needed for license reporting
5864type LicenseReporterConfig struct {
5965 Period time.Duration
6066 K8sClientReader kubernetes.Interface
6167 PodNSName types.NamespacedName
68+ EventLog record.EventRecorder
69+ Pod * api_v1.Pod
70+ PlusClient * client.NginxClient
6271}
6372
6473// NewLicenseReporter creates a new LicenseReporter
65- func NewLicenseReporter (client kubernetes.Interface ) * LicenseReporter {
74+ func NewLicenseReporter (client kubernetes.Interface , eventLog record. EventRecorder , pod * api_v1. Pod ) * LicenseReporter {
6675 return & LicenseReporter {
67- config : LicenseReporterConfig {
68- Period : 24 * time .Hour ,
76+ Config : LicenseReporterConfig {
77+ EventLog : eventLog ,
78+ Period : time .Hour ,
6979 K8sClientReader : client ,
7080 PodNSName : types.NamespacedName {Namespace : os .Getenv ("POD_NAMESPACE" ), Name : os .Getenv ("POD_NAME" )},
81+ Pod : pod ,
7182 },
7283 }
7384}
7485
7586// Start begins the license report writer process for NIC
7687func (lr * LicenseReporter ) Start (ctx context.Context ) {
77- wait .JitterUntilWithContext (ctx , lr .collectAndWrite , lr .config .Period , 0.1 , true )
88+ wait .JitterUntilWithContext (ctx , lr .collectAndWrite , lr .Config .Period , 0.1 , true )
7889}
7990
8091func (lr * LicenseReporter ) collectAndWrite (ctx context.Context ) {
8192 l := nl .LoggerFromContext (ctx )
82- clusterID , err := clusterInfo .GetClusterID (ctx , lr .config .K8sClientReader )
93+ clusterID , err := clusterInfo .GetClusterID (ctx , lr .Config .K8sClientReader )
8394 if err != nil {
8495 nl .Errorf (l , "Error collecting ClusterIDS: %v" , err )
8596 }
86- nodeCount , err := clusterInfo .GetNodeCount (ctx , lr .config .K8sClientReader )
97+ nodeCount , err := clusterInfo .GetNodeCount (ctx , lr .Config .K8sClientReader )
8798 if err != nil {
8899 nl .Errorf (l , "Error collecting ClusterNodeCount: %v" , err )
89100 }
90- installationID , err := clusterInfo .GetInstallationID (ctx , lr .config .K8sClientReader , lr .config .PodNSName )
101+ installationID , err := clusterInfo .GetInstallationID (ctx , lr .Config .K8sClientReader , lr .Config .PodNSName )
91102 if err != nil {
92103 nl .Errorf (l , "Error collecting InstallationID: %v" , err )
93104 }
94105 info := newLicenseInfo (clusterID , installationID , nodeCount )
95106 writeLicenseInfo (l , info )
107+ if lr .Config .PlusClient != nil {
108+ lr .checkLicenseExpiry (ctx )
109+ }
110+ }
111+
112+ func (lr * LicenseReporter ) checkLicenseExpiry (ctx context.Context ) {
113+ l := nl .LoggerFromContext (ctx )
114+ licenseData , err := lr .Config .PlusClient .GetNginxLicense (context .Background ())
115+ if err != nil {
116+ nl .Errorf (l , "could not get license data, %v" , err )
117+ return
118+ }
119+ var licenseEventText string
120+ if expiring , days := licenseExpiring (licenseData ); expiring {
121+ licenseEventText = fmt .Sprintf ("License expiring in %d day(s)" , days )
122+ nl .Warn (l , licenseEventText )
123+ lr .Config .EventLog .Event (lr .Config .Pod , api_v1 .EventTypeWarning , "LicenseExpiry" , licenseEventText )
124+ }
125+ var usageGraceEventText string
126+ if ending , days := usageGraceEnding (licenseData ); ending {
127+ usageGraceEventText = fmt .Sprintf ("Usage reporting grace period ending in %d day(s)" , days )
128+ nl .Warn (l , usageGraceEventText )
129+ lr .Config .EventLog .Event (lr .Config .Pod , api_v1 .EventTypeWarning , "UsageGraceEnding" , usageGraceEventText )
130+ }
131+ }
132+
133+ func licenseExpiring (licenseData * client.NginxLicense ) (bool , int64 ) {
134+ expiry := time .Unix (int64 (licenseData .ActiveTill ), 0 ) //nolint:gosec
135+ now := time .Now ()
136+ timeUntilLicenseExpiry := expiry .Sub (now )
137+ daysUntilLicenseExpiry := int64 (timeUntilLicenseExpiry .Hours () / 24 )
138+ expiryDays := int64 (expiryThreshold .Hours () / 24 )
139+ return daysUntilLicenseExpiry < expiryDays , daysUntilLicenseExpiry
140+ }
141+
142+ func usageGraceEnding (licenseData * client.NginxLicense ) (bool , int64 ) {
143+ grace := time .Second * time .Duration (licenseData .Reporting .Grace ) //nolint:gosec
144+ daysUntilUsageGraceEnds := int64 (grace .Hours () / 24 )
145+ expiryDays := int64 (expiryThreshold .Hours () / 24 )
146+ return daysUntilUsageGraceEnds < expiryDays , daysUntilUsageGraceEnds
96147}
0 commit comments