Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Granular Policy count for telemetry #5489

Merged
merged 8 commits into from
May 3, 2024
9 changes: 8 additions & 1 deletion docs/content/overview/product-telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ These are the data points collected and reported by NGINX Ingress Controller:
- **Services** Number of Services referenced by VirtualServers, VirtualServerRoutes, TransportServers and Ingresses.
- **Ingresses** The number of Ingress resources managed by the NGINX Ingress Controller.
- **IngressClasses** Number of Ingress Classes in the cluster.
- **Policies** Number of policy resources managed by NGINX Ingress Controller
- **AccessControlPolicies** Number of AccessControl policies.
- **RateLimitPolicies** Number of RateLimit policies.
- **JWTAuthPolicies** Number of JWTAuth policies.
- **BasicAuthPolicies** Number of BasicAuth policies.
- **IngressMTLSPolicies** Number of IngressMTLS policies.
- **EgressMTLSPolicies** Number of EgressMTLS policies.
- **OIDCPolicies** Number of OIDC policies.
- **WAFPolicies** Number of WAF policies.
- **GlobalConfiguration** Represents the use of a GlobalConfiguration resource.

## Opt out
Expand Down
38 changes: 34 additions & 4 deletions internal/telemetry/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,42 @@ func (c *Collector) IngressClassCount(ctx context.Context) (int, error) {
return len(ic.Items), nil
}

// PolicyCount returns number of Policies watched by NIC.
func (c *Collector) PolicyCount() int {
// PolicyCount returns the count in each Policy
func (c *Collector) PolicyCount() map[string]int {
policyCounters := make(map[string]int)

if c.Config.Policies == nil {
return 0
return policyCounters
}

policies := c.Config.Policies()
if policies == nil {
return policyCounters
}

for _, policy := range policies {
spec := policy.Spec

switch {
case spec.AccessControl != nil:
policyCounters["AccessControl"]++
case spec.RateLimit != nil:
policyCounters["RateLimit"]++
case spec.JWTAuth != nil:
policyCounters["JWTAuth"]++
case spec.BasicAuth != nil:
policyCounters["BasicAuth"]++
case spec.IngressMTLS != nil:
policyCounters["IngressMTLS"]++
case spec.EgressMTLS != nil:
policyCounters["EgressMTLS"]++
case spec.OIDC != nil:
policyCounters["OIDC"]++
case spec.WAF != nil:
policyCounters["WAF"]++
}
}
return len(c.Config.Policies())
return policyCounters
}

// lookupPlatform takes a string representing a K8s PlatformID
Expand Down
56 changes: 43 additions & 13 deletions internal/telemetry/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,23 @@ func (c *Collector) Collect(ctx context.Context) {
ClusterNodeCount: int64(report.ClusterNodeCount),
},
NICResourceCounts{
VirtualServers: int64(report.VirtualServers),
VirtualServerRoutes: int64(report.VirtualServerRoutes),
TransportServers: int64(report.TransportServers),
Replicas: int64(report.NICReplicaCount),
Secrets: int64(report.Secrets),
Services: int64(report.ServiceCount),
Ingresses: int64(report.IngressCount),
IngressClasses: int64(report.IngressClassCount),
Policies: int64(report.PolicyCount),
GlobalConfiguration: report.GlobalConfiguration,
VirtualServers: int64(report.VirtualServers),
VirtualServerRoutes: int64(report.VirtualServerRoutes),
TransportServers: int64(report.TransportServers),
Replicas: int64(report.NICReplicaCount),
Secrets: int64(report.Secrets),
Services: int64(report.ServiceCount),
Ingresses: int64(report.IngressCount),
IngressClasses: int64(report.IngressClassCount),
AccessControlPolicies: int64(report.AccessControlCount),
RateLimitPolicies: int64(report.RateLimitCount),
JWTAuthPolicies: int64(report.JWTAuthCount),
BasicAuthPolicies: int64(report.BasicAuthCount),
IngressMTLSPolicies: int64(report.IngressMTLSCount),
EgressMTLSPolicies: int64(report.EgressMTLSCount),
OIDCPolicies: int64(report.OIDCCount),
WAFPolicies: int64(report.WAFCount),
GlobalConfiguration: report.GlobalConfiguration,
},
}

Expand Down Expand Up @@ -155,7 +162,14 @@ type Report struct {
Secrets int
IngressCount int
IngressClassCount int
PolicyCount int
AccessControlCount int
RateLimitCount int
JWTAuthCount int
BasicAuthCount int
IngressMTLSCount int
EgressMTLSCount int
OIDCCount int
WAFCount int
GlobalConfiguration bool
}

Expand Down Expand Up @@ -212,7 +226,16 @@ func (c *Collector) BuildReport(ctx context.Context) (Report, error) {
glog.Errorf("Error collecting telemetry data: Ingress Classes: %v", err)
}

policyCount := c.PolicyCount()
policies := c.PolicyCount()

accessControlCount := policies["AccessControl"]
rateLimitCount := policies["RateLimit"]
jwtAuthCount := policies["JWTAuth"]
basicAuthCount := policies["BasicAuth"]
ingressMTLSCount := policies["IngressMTLS"]
egressMTLSCount := policies["EgressMTLS"]
oidcCount := policies["OIDC"]
wafCount := policies["WAF"]

return Report{
Name: "NIC",
Expand All @@ -231,7 +254,14 @@ func (c *Collector) BuildReport(ctx context.Context) (Report, error) {
Secrets: secretCount,
IngressCount: ingressCount,
IngressClassCount: ingressClassCount,
PolicyCount: policyCount,
AccessControlCount: accessControlCount,
RateLimitCount: rateLimitCount,
JWTAuthCount: jwtAuthCount,
BasicAuthCount: basicAuthCount,
IngressMTLSCount: ingressMTLSCount,
EgressMTLSCount: egressMTLSCount,
OIDCCount: oidcCount,
WAFCount: wafCount,
GlobalConfiguration: c.Config.GlobalConfiguration,
}, err
}
198 changes: 112 additions & 86 deletions internal/telemetry/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,91 +250,85 @@ func TestCollectClusterVersion(t *testing.T) {
}
}

func TestCollectMultiplePolicies(t *testing.T) {
func TestCollectPolicyCount(t *testing.T) {
t.Parallel()

fn := func() []*conf_v1.Policy {
return []*conf_v1.Policy{&policy1, &policy2, &policy3}
}

cfg := telemetry.CollectorConfig{
Policies: fn,
}
collector, err := telemetry.NewCollector(cfg)
if err != nil {
t.Fatal(err)
}
got := collector.PolicyCount()

want := 3

if want != got {
t.Errorf("want %d, got %d", want, got)
}
}

func TestCollectSinglePolicy(t *testing.T) {
t.Parallel()

fn := func() []*conf_v1.Policy {
return []*conf_v1.Policy{&policy1}
}

cfg := telemetry.CollectorConfig{
Policies: fn,
}
collector, err := telemetry.NewCollector(cfg)
if err != nil {
t.Fatal(err)
}
got := collector.PolicyCount()

want := 1

if want != got {
t.Errorf("want %d, got %d", want, got)
}
}

func TestCollectNoPolicies(t *testing.T) {
t.Parallel()

fn := func() []*conf_v1.Policy {
return []*conf_v1.Policy{}
}

cfg := telemetry.CollectorConfig{
Policies: fn,
}
collector, err := telemetry.NewCollector(cfg)
if err != nil {
t.Fatal(err)
}
got := collector.PolicyCount()

want := 0

if want != got {
t.Errorf("want %d, got %d", want, got)
testCases := []struct {
name string
policies func() []*conf_v1.Policy
want int
}{
{
name: "SinglePolicy",
policies: func() []*conf_v1.Policy {
return []*conf_v1.Policy{&egressMTLSPolicy}
},
want: 1,
},
{
name: "MultiplePolicies",
policies: func() []*conf_v1.Policy {
return []*conf_v1.Policy{&rateLimitPolicy, &wafPolicy, &oidcPolicy}
},
want: 3,
},
{
name: "MultipleSamePolicies",
policies: func() []*conf_v1.Policy {
return []*conf_v1.Policy{&rateLimitPolicy, &rateLimitPolicy}
},
want: 2,
},
{
name: "SingleInvalidPolicy",
policies: func() []*conf_v1.Policy {
return []*conf_v1.Policy{&rateLimitPolicyInvalid}
},
want: 0,
},
{
name: "MultiplePoliciesOneValidOneInvalid",
policies: func() []*conf_v1.Policy {
return []*conf_v1.Policy{&rateLimitPolicy, &rateLimitPolicyInvalid}
},
want: 1,
},
{
name: "NoPolicies",
policies: func() []*conf_v1.Policy { return []*conf_v1.Policy{} },
want: 0,
},
{
name: "NilPolicies",
policies: nil,
want: 0,
},
{
name: "NilPolicyValue",
policies: func() []*conf_v1.Policy { return nil },
want: 0,
},
}
}

func TestCollectPolicyWithNilFunction(t *testing.T) {
t.Parallel()

cfg := telemetry.CollectorConfig{
Policies: nil,
}
collector, err := telemetry.NewCollector(cfg)
if err != nil {
t.Fatal(err)
}
got := collector.PolicyCount()
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var got int
cfg := telemetry.CollectorConfig{
Policies: tc.policies,
}
collector, err := telemetry.NewCollector(cfg)
if err != nil {
t.Fatal(err)
}

want := 0
for _, polCount := range collector.PolicyCount() {
got += polCount
}

if want != got {
t.Errorf("want %d, got %d", want, got)
if tc.want != got {
t.Errorf("want %d policies, got %d", tc.want, got)
}
})
}
}

Expand Down Expand Up @@ -1669,31 +1663,33 @@ var telemetryNICData = tel.Data{

// Policies used for testing for PolicyCount method
var (
policy1 = conf_v1.Policy{
rateLimitPolicy = conf_v1.Policy{
TypeMeta: metaV1.TypeMeta{
Kind: "Policy",
APIVersion: "k8s.nginx.org/v1",
},
ObjectMeta: metaV1.ObjectMeta{
Name: "rate-limit-policy1",
Name: "rate-limit-policy3",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{},
Spec: conf_v1.PolicySpec{
RateLimit: &conf_v1.RateLimit{},
},
Status: conf_v1.PolicyStatus{},
}
policy2 = conf_v1.Policy{
rateLimitPolicyInvalid = conf_v1.Policy{
TypeMeta: metaV1.TypeMeta{
Kind: "Policy",
APIVersion: "k8s.nginx.org/v1",
},
ObjectMeta: metaV1.ObjectMeta{
Name: "rate-limit-policy2",
Name: "INVALID-rate-limit-policy",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{},
Status: conf_v1.PolicyStatus{},
}
policy3 = conf_v1.Policy{
egressMTLSPolicy = conf_v1.Policy{
TypeMeta: metaV1.TypeMeta{
Kind: "Policy",
APIVersion: "k8s.nginx.org/v1",
Expand All @@ -1702,7 +1698,37 @@ var (
Name: "rate-limit-policy3",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{},
Spec: conf_v1.PolicySpec{
EgressMTLS: &conf_v1.EgressMTLS{},
},
Status: conf_v1.PolicyStatus{},
}
oidcPolicy = conf_v1.Policy{
TypeMeta: metaV1.TypeMeta{
Kind: "Policy",
APIVersion: "k8s.nginx.org/v1",
},
ObjectMeta: metaV1.ObjectMeta{
Name: "rate-limit-policy3",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
OIDC: &conf_v1.OIDC{},
},
Status: conf_v1.PolicyStatus{},
}
wafPolicy = conf_v1.Policy{
TypeMeta: metaV1.TypeMeta{
Kind: "Policy",
APIVersion: "k8s.nginx.org/v1",
},
ObjectMeta: metaV1.ObjectMeta{
Name: "rate-limit-policy3",
Namespace: "default",
},
Spec: conf_v1.PolicySpec{
WAF: &conf_v1.WAF{},
},
Status: conf_v1.PolicyStatus{},
}
)
Loading
Loading