@@ -3,19 +3,25 @@ package licensereporting
3
3
import (
4
4
"context"
5
5
"encoding/json"
6
+ "fmt"
6
7
"log/slog"
7
8
"os"
8
9
"path/filepath"
9
10
"time"
10
11
11
12
nl "github.com/nginxinc/kubernetes-ingress/internal/logger"
13
+ "github.com/nginxinc/nginx-plus-go-client/v2/client"
12
14
13
15
clusterInfo "github.com/nginxinc/kubernetes-ingress/internal/common_cluster_info"
16
+ api_v1 "k8s.io/api/core/v1"
14
17
"k8s.io/apimachinery/pkg/types"
15
18
"k8s.io/apimachinery/pkg/util/wait"
16
19
"k8s.io/client-go/kubernetes"
20
+ "k8s.io/client-go/tools/record"
17
21
)
18
22
23
+ const expiryThreshold = 30 * (time .Hour * 24 )
24
+
19
25
var (
20
26
reportingDir = "/etc/nginx/reporting"
21
27
reportingFile = "tracking.info"
@@ -51,46 +57,91 @@ func writeLicenseInfo(l *slog.Logger, info *licenseInfo) {
51
57
52
58
// LicenseReporter can start the license reporting process
53
59
type LicenseReporter struct {
54
- config LicenseReporterConfig
60
+ Config LicenseReporterConfig
55
61
}
56
62
57
63
// LicenseReporterConfig contains the information needed for license reporting
58
64
type LicenseReporterConfig struct {
59
65
Period time.Duration
60
66
K8sClientReader kubernetes.Interface
61
67
PodNSName types.NamespacedName
68
+ EventLog record.EventRecorder
69
+ Pod * api_v1.Pod
70
+ PlusClient * client.NginxClient
62
71
}
63
72
64
73
// 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 {
66
75
return & LicenseReporter {
67
- config : LicenseReporterConfig {
68
- Period : 24 * time .Hour ,
76
+ Config : LicenseReporterConfig {
77
+ EventLog : eventLog ,
78
+ Period : time .Hour ,
69
79
K8sClientReader : client ,
70
80
PodNSName : types.NamespacedName {Namespace : os .Getenv ("POD_NAMESPACE" ), Name : os .Getenv ("POD_NAME" )},
81
+ Pod : pod ,
71
82
},
72
83
}
73
84
}
74
85
75
86
// Start begins the license report writer process for NIC
76
87
func (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 )
78
89
}
79
90
80
91
func (lr * LicenseReporter ) collectAndWrite (ctx context.Context ) {
81
92
l := nl .LoggerFromContext (ctx )
82
- clusterID , err := clusterInfo .GetClusterID (ctx , lr .config .K8sClientReader )
93
+ clusterID , err := clusterInfo .GetClusterID (ctx , lr .Config .K8sClientReader )
83
94
if err != nil {
84
95
nl .Errorf (l , "Error collecting ClusterIDS: %v" , err )
85
96
}
86
- nodeCount , err := clusterInfo .GetNodeCount (ctx , lr .config .K8sClientReader )
97
+ nodeCount , err := clusterInfo .GetNodeCount (ctx , lr .Config .K8sClientReader )
87
98
if err != nil {
88
99
nl .Errorf (l , "Error collecting ClusterNodeCount: %v" , err )
89
100
}
90
- installationID , err := clusterInfo .GetInstallationID (ctx , lr .config .K8sClientReader , lr .config .PodNSName )
101
+ installationID , err := clusterInfo .GetInstallationID (ctx , lr .Config .K8sClientReader , lr .Config .PodNSName )
91
102
if err != nil {
92
103
nl .Errorf (l , "Error collecting InstallationID: %v" , err )
93
104
}
94
105
info := newLicenseInfo (clusterID , installationID , nodeCount )
95
106
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
96
147
}
0 commit comments