Skip to content

Commit

Permalink
NETOBSERV-1614: Metrics API validation webhook
Browse files Browse the repository at this point in the history
Signed-off-by: Mohamed Mahmoud <[email protected]>
  • Loading branch information
msherif1234 committed Apr 19, 2024
1 parent b26356d commit 161b813
Show file tree
Hide file tree
Showing 13 changed files with 221 additions and 21 deletions.
7 changes: 7 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Code generated by tool. DO NOT EDIT.
# This file is used to track the info used to scaffold your project
# and allow the plugins properly work.
# More info: https://book.kubebuilder.io/reference/project-config.html
domain: netobserv.io
layout:
- go.kubebuilder.io/v3
Expand Down Expand Up @@ -30,4 +34,7 @@ resources:
kind: FlowMetric
path: github.com/netobserv/network-observability-operator/apis/flowmetrics/v1alpha1
version: v1alpha1
webhooks:
validation: true
webhookVersion: v1
version: "3"
6 changes: 3 additions & 3 deletions apis/flowmetrics/v1alpha1/flowmetric_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ type FlowMetricSpec struct {

// `valueField` is the flow field that must be used as a value for this metric. This field must hold numeric values.
// Leave empty to count flows rather than a specific value per flow.
// Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/networking/network_observability/json-flows-format-reference.html.
// Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/observability/network_observability/json-flows-format-reference.html.
// +optional
ValueField string `json:"valueField,omitempty"`

// `filters` is a list of fields and values used to restrict which flows are taken into account. Oftentimes, these filters must
// be used to eliminate duplicates: `Duplicate != "true"` and `FlowDirection = "0"`.
// Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/networking/network_observability/json-flows-format-reference.html.
// Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/observability/network_observability/json-flows-format-reference.html.
// +optional
Filters []MetricFilter `json:"filters"`

Expand All @@ -89,7 +89,7 @@ type FlowMetricSpec struct {
// It must be done carefully as it impacts the metric cardinality (cf https://rhobs-handbook.netlify.app/products/openshiftmonitoring/telemetry.md/#what-is-the-cardinality-of-a-metric).
// In general, avoid setting very high cardinality labels such as IP or MAC addresses.
// "SrcK8S_OwnerName" or "DstK8S_OwnerName" should be preferred over "SrcK8S_Name" or "DstK8S_Name" as much as possible.
// Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/network_observability/json-flows-format-reference.html.
// Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/observability/network_observability/json-flows-format-reference.html.
// +optional
Labels []string `json:"labels"`

Expand Down
56 changes: 56 additions & 0 deletions apis/flowmetrics/v1alpha1/flowmetric_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package v1alpha1

import (
"fmt"

"github.com/netobserv/network-observability-operator/pkg/helper"

"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
)

// log is for logging in this package.
var flowmetriclog = logf.Log.WithName("flowmetric-resource")

func (r *FlowMetric) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

//+kubebuilder:webhook:path=/validate-netobserv-io-v1alpha1-flowmetric,mutating=false,failurePolicy=fail,sideEffects=None,groups=netobserv.io,resources=flowmetrics,verbs=create;update,versions=v1alpha1,name=flowmetricvalidationwebhook.netobserv.io,admissionReviewVersions=v1

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *FlowMetric) ValidateCreate() error {
flowmetriclog.Info("validate create", "name", r.Name)
return r.validateFlowMetric()
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *FlowMetric) ValidateUpdate(_ runtime.Object) error {
flowmetriclog.Info("validate update", "name", r.Name)
return r.validateFlowMetric()
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *FlowMetric) ValidateDelete() error {
flowmetriclog.Info("validate delete", "name", r.Name)
return nil
}

func (r *FlowMetric) validateFlowMetric() error {

for _, f := range r.Spec.Filters {
if !helper.FindFilter(f.Field) {
return fmt.Errorf("invalid filter name %s", f.Field)
}
}

for _, l := range r.Spec.Labels {
if !helper.FindFilter(l) {
return fmt.Errorf("invalid label name %s", l)
}
}
return nil
}
95 changes: 95 additions & 0 deletions apis/flowmetrics/v1alpha1/flowmetric_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package v1alpha1

import (
"fmt"
"strings"
"testing"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestFlowMetric(t *testing.T) {
tests := []struct {
desc string
m *FlowMetric
expectedError string
}{
{
desc: "Invalid FlowMetric Filter",
m: &FlowMetric{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "test-namespace",
},
Spec: FlowMetricSpec{
Filters: []MetricFilter{
{
Field: "test",
},
},
},
},
expectedError: "invalid filter name",
},
{
desc: "Valid FlowMetric Filter",
m: &FlowMetric{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "test-namespace",
},
Spec: FlowMetricSpec{
Filters: []MetricFilter{
{
Field: "DstK8S_Zone",
},
},
},
},
expectedError: "",
},
{
desc: "Invalid FlowMetric Label",
m: &FlowMetric{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "test-namespace",
},
Spec: FlowMetricSpec{
Labels: []string{
"test",
},
},
},
expectedError: "invalid label name",
},
{
desc: "Valid FlowMetric Label",
m: &FlowMetric{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "test-namespace",
},
Spec: FlowMetricSpec{
Labels: []string{
"DstK8S_Zone",
},
},
},
expectedError: "",
},
}

for _, test := range tests {
err := test.m.validateFlowMetric()
if err == nil {
if test.expectedError != "" {
t.Errorf("%s: ValidateFlowMetric failed, no error found while expected: \"%s\"", test.desc, test.expectedError)
}
} else {
if !strings.Contains(fmt.Sprint(err), test.expectedError) {
t.Errorf("%s: ValidateFlowMetric failed, expected error: \"%s\" to contain: \"%s\"", test.desc, err, test.expectedError)
}
}
}
}
2 changes: 1 addition & 1 deletion apis/flowmetrics/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions bundle/manifests/flows.netobserv.io_flowmetrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ spec:
description: |-
`filters` is a list of fields and values used to restrict which flows are taken into account. Oftentimes, these filters must
be used to eliminate duplicates: `Duplicate != "true"` and `FlowDirection = "0"`.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/networking/network_observability/json-flows-format-reference.html.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/observability/network_observability/json-flows-format-reference.html.
items:
properties:
field:
Expand Down Expand Up @@ -103,7 +103,7 @@ spec:
It must be done carefully as it impacts the metric cardinality (cf https://rhobs-handbook.netlify.app/products/openshiftmonitoring/telemetry.md/#what-is-the-cardinality-of-a-metric).
In general, avoid setting very high cardinality labels such as IP or MAC addresses.
"SrcK8S_OwnerName" or "DstK8S_OwnerName" should be preferred over "SrcK8S_Name" or "DstK8S_Name" as much as possible.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/network_observability/json-flows-format-reference.html.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/observability/network_observability/json-flows-format-reference.html.
items:
type: string
type: array
Expand All @@ -124,7 +124,7 @@ spec:
description: |-
`valueField` is the flow field that must be used as a value for this metric. This field must hold numeric values.
Leave empty to count flows rather than a specific value per flow.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/networking/network_observability/json-flows-format-reference.html.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/observability/network_observability/json-flows-format-reference.html.
type: string
required:
- metricName
Expand Down
6 changes: 3 additions & 3 deletions config/crd/bases/flows.netobserv.io_flowmetrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ spec:
description: |-
`filters` is a list of fields and values used to restrict which flows are taken into account. Oftentimes, these filters must
be used to eliminate duplicates: `Duplicate != "true"` and `FlowDirection = "0"`.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/networking/network_observability/json-flows-format-reference.html.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/observability/network_observability/json-flows-format-reference.html.
items:
properties:
field:
Expand Down Expand Up @@ -103,7 +103,7 @@ spec:
It must be done carefully as it impacts the metric cardinality (cf https://rhobs-handbook.netlify.app/products/openshiftmonitoring/telemetry.md/#what-is-the-cardinality-of-a-metric).
In general, avoid setting very high cardinality labels such as IP or MAC addresses.
"SrcK8S_OwnerName" or "DstK8S_OwnerName" should be preferred over "SrcK8S_Name" or "DstK8S_Name" as much as possible.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/network_observability/json-flows-format-reference.html.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/observability/network_observability/json-flows-format-reference.html.
items:
type: string
type: array
Expand All @@ -124,7 +124,7 @@ spec:
description: |-
`valueField` is the flow field that must be used as a value for this metric. This field must hold numeric values.
Leave empty to count flows rather than a specific value per flow.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/networking/network_observability/json-flows-format-reference.html.
Refer to the documentation for the list of available fields: https://docs.openshift.com/container-platform/latest/observability/network_observability/json-flows-format-reference.html.
type: string
required:
- metricName
Expand Down
20 changes: 20 additions & 0 deletions config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,23 @@ webhooks:
resources:
- flowcollectors
sideEffects: None
- admissionReviewVersions:
- v1
clientConfig:
service:
name: webhook-service
namespace: system
path: /validate-netobserv-io-v1alpha1-flowmetric
failurePolicy: Fail
name: flowmetricvalidationwebhook.netobserv.io
rules:
- apiGroups:
- netobserv.io
apiVersions:
- v1alpha1
operations:
- CREATE
- UPDATE
resources:
- flowmetrics
sideEffects: None
5 changes: 5 additions & 0 deletions controllers/consoleplugin/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package config

import (
_ "embed"

"github.com/netobserv/flowlogs-pipeline/pkg/api"
flowslatest "github.com/netobserv/network-observability-operator/apis/flowcollector/v1beta2"
)
Expand Down Expand Up @@ -96,3 +98,6 @@ type PluginConfig struct {
Loki LokiConfig `yaml:"loki" json:"loki"`
Frontend FrontendConfig `yaml:"frontend" json:"frontend"`
}

//go:embed static-frontend-config.yaml
var StaticFrontendConfig []byte
16 changes: 6 additions & 10 deletions controllers/consoleplugin/consoleplugin_objects.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package consoleplugin

import (
_ "embed"
"fmt"
"hash/fnv"
"path/filepath"
Expand All @@ -18,7 +17,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"

flowslatest "github.com/netobserv/network-observability-operator/apis/flowcollector/v1beta2"
config "github.com/netobserv/network-observability-operator/controllers/consoleplugin/config"
cfg "github.com/netobserv/network-observability-operator/controllers/consoleplugin/config"
"github.com/netobserv/network-observability-operator/controllers/constants"
"github.com/netobserv/network-observability-operator/controllers/ebpf"
"github.com/netobserv/network-observability-operator/pkg/helper"
Expand Down Expand Up @@ -310,7 +309,7 @@ func (b *builder) metricsService() *corev1.Service {
}
}

func (b *builder) setLokiConfig(lconf *config.LokiConfig) {
func (b *builder) setLokiConfig(lconf *cfg.LokiConfig) {
lconf.URL = b.loki.QuerierURL
statusURL := b.loki.StatusURL
if lconf.URL != statusURL {
Expand Down Expand Up @@ -353,7 +352,7 @@ func (b *builder) setLokiConfig(lconf *config.LokiConfig) {
}
}

func (b *builder) setFrontendConfig(fconf *config.FrontendConfig) error {
func (b *builder) setFrontendConfig(fconf *cfg.FrontendConfig) error {
var err error
dedupJustMark, err := strconv.ParseBool(ebpf.DedupeJustMarkDefault)
if err != nil {
Expand Down Expand Up @@ -395,7 +394,7 @@ func (b *builder) setFrontendConfig(fconf *config.FrontendConfig) error {
fconf.QuickFilters = b.desired.ConsolePlugin.QuickFilters
fconf.AlertNamespaces = []string{b.namespace}
fconf.Sampling = helper.GetSampling(b.desired)
fconf.Deduper = config.Deduper{
fconf.Deduper = cfg.Deduper{
Mark: dedupJustMark,
Merge: dedupMerge,
}
Expand All @@ -411,13 +410,10 @@ func (b *builder) setFrontendConfig(fconf *config.FrontendConfig) error {
return nil
}

//go:embed config/static-frontend-config.yaml
var staticFrontendConfig []byte

// returns a configmap with a digest of its configuration contents, which will be used to
// detect any configuration change
func (b *builder) configMap() (*corev1.ConfigMap, string, error) {
config := config.PluginConfig{}
config := cfg.PluginConfig{}
// configure server
config.Server.CertPath = "/var/serving-cert/tls.crt"
config.Server.KeyPath = "/var/serving-cert/tls.key"
Expand All @@ -427,7 +423,7 @@ func (b *builder) configMap() (*corev1.ConfigMap, string, error) {
b.setLokiConfig(&config.Loki)

// configure frontend from embedded static file
err := yaml.Unmarshal(staticFrontendConfig, &config.Frontend)
err := yaml.Unmarshal(cfg.StaticFrontendConfig, &config.Frontend)
if err != nil {
return nil, "", err
}
Expand Down
2 changes: 1 addition & 1 deletion controllers/consoleplugin/consoleplugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ func TestHTTPClientConfig(t *testing.T) {

func TestNoMissingFields(t *testing.T) {
var cfg config.FrontendConfig
err := yaml.Unmarshal(staticFrontendConfig, &cfg)
err := yaml.Unmarshal(config.StaticFrontendConfig, &cfg)
assert.NoError(t, err)

hasField := func(name string) bool {
Expand Down
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ func main() {
setupLog.Error(err, "unable to create v1beta2 webhook", "webhook", "FlowCollector")
os.Exit(1)
}
if err = (&metricsv1alpha1.FlowMetric{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "FlowMetric")
os.Exit(1)
}
//+kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Expand Down
Loading

0 comments on commit 161b813

Please sign in to comment.