Skip to content

Commit

Permalink
Merge pull request #8 from suleymanakbas91/route-ingress-metrics
Browse files Browse the repository at this point in the history
Bug 1962502: Create custom metrics to list unmanaged Routes and Ingresses without IngressClassName
  • Loading branch information
openshift-merge-robot committed Oct 25, 2022
2 parents c232643 + ffe8f7d commit a4731c8
Show file tree
Hide file tree
Showing 3 changed files with 383 additions and 23 deletions.
65 changes: 42 additions & 23 deletions pkg/route/ingress/ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"k8s.io/component-base/metrics/legacyregistry"
"k8s.io/kubernetes/pkg/api/legacyscheme"

routev1 "github.com/openshift/api/route/v1"
Expand Down Expand Up @@ -95,6 +96,11 @@ type Controller struct {
// expectationDelay controls how long the controller waits to observe its
// own creates. Exposed only for testing.
expectationDelay time.Duration

// Prometheus metrics
metricsCreated bool
metricsCreateOnce sync.Once
metricsCreateLock sync.RWMutex
}

// expectations track an upcoming change to a named resource related
Expand Down Expand Up @@ -250,6 +256,11 @@ func NewController(eventsClient kv1core.EventsGetter, routeClient routeclient.Ro
},
})

if !c.MetricsCreated() {
legacyregistry.MustRegister(c)
}
klog.Info("ingress-to-route metrics registered with prometheus")

return c
}

Expand Down Expand Up @@ -372,30 +383,12 @@ func (c *Controller) sync(key queueKey) error {
return err
}

// If the ingress specifies an ingressclass and the ingressclass does
// not specify openshift.io/ingress-to-route as its controller, ignore
// the ingress.
var ingressClassName *string
if v, ok := ingress.Annotations["kubernetes.io/ingress.class"]; ok {
ingressClassName = &v
} else {
ingressClassName = ingress.Spec.IngressClassName
managed, err := c.ingressManaged(ingress)
if err != nil {
return err
}
if ingressClassName != nil {
ingressclass, err := c.ingressclassLister.Get(*ingressClassName)
if kerrors.IsNotFound(err) {
return nil
}
if err != nil {
return err
}
// TODO Replace "openshift.io/ingress-to-route" with
// routev1.IngressToRouteIngressClassControllerName once
// openshift-controller-manager bumps openshift/api to a version
// that defines it.
if ingressclass.Spec.Controller != "openshift.io/ingress-to-route" {
return nil
}
if !managed {
return nil
}

// find all matching routes
Expand Down Expand Up @@ -576,6 +569,32 @@ func hasIngressOwnerRef(owners []metav1.OwnerReference) (string, bool) {
return "", false
}

func (c *Controller) ingressManaged(ingress *networkingv1.Ingress) (bool, error) {
// If the ingress specifies an ingressclass and the ingressclass does
// not specify openshift.io/ingress-to-route as its controller, ignore
// the ingress.
var ingressClassName *string
if v, ok := ingress.Annotations["kubernetes.io/ingress.class"]; ok {
ingressClassName = &v
} else {
ingressClassName = ingress.Spec.IngressClassName
}
if ingressClassName != nil {
ingressclass, err := c.ingressclassLister.Get(*ingressClassName)
if kerrors.IsNotFound(err) {
return false, nil
}
if err != nil {
return false, err
}
if ingressclass.Spec.Controller != routev1.IngressToRouteIngressClassControllerName {
return false, nil
}
}

return true, nil
}

func newRouteForIngress(
ingress *networkingv1.Ingress,
rule *networkingv1.IngressRule,
Expand Down
108 changes: 108 additions & 0 deletions pkg/route/ingress/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package ingress

import (
"github.com/blang/semver/v4"
"github.com/prometheus/client_golang/prometheus"

"k8s.io/apimachinery/pkg/labels"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)

const (
routeController = "openshift_ingress_to_route_controller"
metricRouteWithUnmanagedOwner = routeController + "_route_with_unmanaged_owner"
metricIngressWithoutClassName = routeController + "_ingress_without_class_name"
)

var (
unmanagedRoutes = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: metricRouteWithUnmanagedOwner,
Help: "Report the number of routes owned by unmanaged ingresses.",
}, []string{"name", "namespace", "host"})

ingressesWithoutClassName = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: metricIngressWithoutClassName,
Help: "Report the number of ingresses that do not specify ingressClassName.",
}, []string{"name"})
)

func (c *Controller) Create(v *semver.Version) bool {
c.metricsCreateOnce.Do(func() {
c.metricsCreateLock.Lock()
defer c.metricsCreateLock.Unlock()
c.metricsCreated = true
})
return c.MetricsCreated()
}

func (c *Controller) MetricsCreated() bool {
return c.metricsCreated
}

func (c *Controller) ClearState() {
c.metricsCreateLock.Lock()
defer c.metricsCreateLock.Unlock()
c.metricsCreated = false
}

// FQName returns the fully-qualified metric name of the collector.
func (c *Controller) FQName() string {
return routeController
}

func (c *Controller) Describe(ch chan<- *prometheus.Desc) {
unmanagedRoutes.Describe(ch)
ingressesWithoutClassName.Describe(ch)
}

func (c *Controller) Collect(ch chan<- prometheus.Metric) {
// collect ingresses that do not specify ingressClassName
ingressInstances, err := c.ingressLister.List(labels.Everything())
if err != nil {
utilruntime.HandleError(err)
return
}

for _, ingressInstance := range ingressInstances {
labelVal := 0
icName := ingressInstance.Spec.IngressClassName
if icName == nil || *icName == "" {
labelVal = 1
}
ingressesWithoutClassName.WithLabelValues(ingressInstance.Name).Set(float64(labelVal))
}

ingressesWithoutClassName.Collect(ch)

// collect routes owned by ingresses no longer managed
routeInstances, err := c.routeLister.List(labels.Everything())
if err != nil {
utilruntime.HandleError(err)
return
}

for _, routeInstance := range routeInstances {
labelVal := 0
if owner, have := hasIngressOwnerRef(routeInstance.OwnerReferences); have {
for _, ingressInstance := range ingressInstances {
ingress, err := c.ingressLister.Ingresses(ingressInstance.Namespace).Get(ingressInstance.Name)
if err != nil || ingress == nil {
continue
}
if ingress.Name == owner {
managed, err := c.ingressManaged(ingress)
if err != nil {
utilruntime.HandleError(err)
return
}
if !managed {
labelVal = 1
}
}
}
}
unmanagedRoutes.WithLabelValues(routeInstance.Name, routeInstance.Namespace, routeInstance.Spec.Host).Set(float64(labelVal))
}

unmanagedRoutes.Collect(ch)
}
Loading

0 comments on commit a4731c8

Please sign in to comment.