From 37d840123920e6a0c17217b89c98f37e7d8c1b2a Mon Sep 17 00:00:00 2001 From: Quang Nguyen Date: Tue, 6 Aug 2024 14:07:33 -0400 Subject: [PATCH] feat: add data aggregation level (#556) # Description This PR introduced the config map option for data aggregation. More details can be found in the docs. ## Related Issue #138 ## Checklist - [x] I have read the [contributing documentation](https://retina.sh/docs/contributing). - [x] I signed and signed-off the commits (`git commit -S -s ...`). See [this documentation](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification) on signing commits. - [x] I have correctly attributed the author(s) of the code. - [x] I have tested the changes locally. - [x] I have followed the project's style guidelines. - [x] I have updated the documentation, if necessary. - [x] I have added tests, if applicable. ## Screenshots (if applicable) or Testing Completed - Added unit tests for parsing data aggregation level from config file - Deploy on cluster with different data aggregation level, verified that on `high`, `packetparser` doesn't attach bpf program to eth0 ## Additional Notes Add any additional notes or context about the pull request here. --- Please refer to the [CONTRIBUTING.md](../CONTRIBUTING.md) file for more information on how to contribute to this project. --------- Signed-off-by: Quang Nguyen --- cmd/hubble/daemon_main_linux.go | 1 + cmd/legacy/daemon.go | 3 + controller/Dockerfile | 16 +-- controller/Dockerfile.windows-2019 | 2 +- controller/Dockerfile.windows-2022 | 4 +- controller/Dockerfile.windows-cgo | 2 +- controller/Dockerfile.windows-native | 4 +- .../retina/templates/agent/configmap.yaml | 1 + .../controller/helm/retina/values.yaml | 3 +- .../helm/retina/templates/configmap.yaml | 2 + .../controller/helm/retina/values.yaml | 1 + docs/concepts/dataAggregation.md | 7 + docs/installation/config.md | 1 + operator/Dockerfile | 2 +- operator/Dockerfile.windows-2019 | 2 +- operator/Dockerfile.windows-2022 | 2 +- pkg/config/config.go | 65 ++++++++- pkg/config/config_test.go | 30 +++- pkg/config/hubble_config_linux.go | 1 + pkg/config/testwith/config.yaml | 1 + .../controllermanager/controllermanager.go | 4 +- .../dropreason/dropreason_linux_test.go | 1 + pkg/plugin/packetparser/packetparser_linux.go | 38 ++--- .../packetparser/packetparser_linux_test.go | 134 ++++++++++++++++++ pkg/servermanager/cell_linux.go | 2 +- 25 files changed, 288 insertions(+), 41 deletions(-) create mode 100644 docs/concepts/dataAggregation.md diff --git a/cmd/hubble/daemon_main_linux.go b/cmd/hubble/daemon_main_linux.go index 74b0b3732c..b0d8e38bbe 100644 --- a/cmd/hubble/daemon_main_linux.go +++ b/cmd/hubble/daemon_main_linux.go @@ -254,6 +254,7 @@ func setupZapLogger(retinaConfig *config.Config, k8sCfg *rest.Config) *log.ZapLo zap.String("version", buildinfo.Version), zap.String("apiserver", k8sCfg.Host), zap.Strings("plugins", retinaConfig.EnabledPlugin), + zap.String("data aggregation level", retinaConfig.DataAggregationLevel.String()), } _, err := log.SetupZapLogger(logOpts, persistentFields...) diff --git a/cmd/legacy/daemon.go b/cmd/legacy/daemon.go index 15843bbd51..bd74073761 100644 --- a/cmd/legacy/daemon.go +++ b/cmd/legacy/daemon.go @@ -111,6 +111,7 @@ func (d *Daemon) Start() error { zap.String("version", buildinfo.Version), zap.String("apiserver", cfg.Host), zap.String("plugins", strings.Join(daemonConfig.EnabledPlugin, `,`)), + zap.String("data aggregation level", daemonConfig.DataAggregationLevel.String()), ) if err != nil { panic(err) @@ -120,6 +121,8 @@ func (d *Daemon) Start() error { metrics.InitializeMetrics() + mainLogger.Info(zap.String("data aggregation level", daemonConfig.DataAggregationLevel.String())) + var tel telemetry.Telemetry if daemonConfig.EnableTelemetry && buildinfo.ApplicationInsightsID != "" { mainLogger.Info("telemetry enabled", zap.String("applicationInsightsID", buildinfo.ApplicationInsightsID)) diff --git a/controller/Dockerfile b/controller/Dockerfile index 12659af3ca..853ddf0a01 100644 --- a/controller/Dockerfile +++ b/controller/Dockerfile @@ -3,19 +3,19 @@ ARG OS_VERSION # pinned base images # mcr.microsoft.com/oss/go/microsoft/golang:1.22.3-1-cbl-mariner2.0 -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/oss/go/microsoft/golang@sha256:8253def0216b87b2994b7ad689aeec7440f6eb67f981e438071d8d67e36ff69f as golang +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/oss/go/microsoft/golang@sha256:8253def0216b87b2994b7ad689aeec7440f6eb67f981e438071d8d67e36ff69f AS golang # mcr.microsoft.com/cbl-mariner/base/core:2.0 -FROM --platform=$TARGETPLATFORM mcr.microsoft.com/cbl-mariner/base/core@sha256:77651116f2e83cf50fddd8a0316945499f8ce6521ff8e94e67539180d1e5975a as mariner-core +FROM --platform=$TARGETPLATFORM mcr.microsoft.com/cbl-mariner/base/core@sha256:77651116f2e83cf50fddd8a0316945499f8ce6521ff8e94e67539180d1e5975a AS mariner-core # mcr.microsoft.com/cbl-mariner/distroless/minimal:2.0 -FROM --platform=$TARGETPLATFORM mcr.microsoft.com/cbl-mariner/distroless/minimal@sha256:63a0a70ceaa1320bc6eb98b81106667d43e46b674731ea8d28e4de1b87e0747f as mariner-distroless +FROM --platform=$TARGETPLATFORM mcr.microsoft.com/cbl-mariner/distroless/minimal@sha256:63a0a70ceaa1320bc6eb98b81106667d43e46b674731ea8d28e4de1b87e0747f AS mariner-distroless # mcr.microsoft.com/windows/servercore:ltsc2019 -FROM --platform=$TARGETPLATFORM mcr.microsoft.com/windows/servercore@sha256:6fdf140282a2f809dae9b13fe441635867f0a27c33a438771673b8da8f3348a4 as ltsc2019 +FROM --platform=$TARGETPLATFORM mcr.microsoft.com/windows/servercore@sha256:6fdf140282a2f809dae9b13fe441635867f0a27c33a438771673b8da8f3348a4 AS ltsc2019 # mcr.microsoft.com/windows/servercore:ltsc2022 -FROM --platform=$TARGETPLATFORM mcr.microsoft.com/windows/servercore@sha256:45952938708fbde6ec0b5b94de68bcdec3f8c838be018536b1e9e5bd95e6b943 as ltsc2022 +FROM --platform=$TARGETPLATFORM mcr.microsoft.com/windows/servercore@sha256:45952938708fbde6ec0b5b94de68bcdec3f8c838be018536b1e9e5bd95e6b943 AS ltsc2022 # build stages @@ -103,7 +103,7 @@ RUN echo "Hubble version: $HUBBLE_VERSION" && \ rm hubble-linux-${HUBBLE_ARCH}.tar.gz && rm hubble-linux-${HUBBLE_ARCH}.tar.gz.sha256sum # init final image -FROM mariner-distroless as init +FROM mariner-distroless AS init COPY --from=init-bin /go/bin/retina/initretina /retina/initretina COPY --from=tools /lib/ /lib COPY --from=tools /usr/lib/ /usr/lib @@ -113,7 +113,7 @@ ENTRYPOINT ["./retina/initretina"] # agent final image # mcr.microsoft.com/cbl-mariner/distroless/minimal:2.0 # mcr.microsoft.com/cbl-mariner/distroless/minimal@sha256:63a0a70ceaa1320bc6eb98b81106667d43e46b674731ea8d28e4de1b87e0747f -FROM mariner-distroless as agent +FROM mariner-distroless AS agent COPY --from=tools /lib/ /lib COPY --from=tools /usr/lib/ /usr/lib COPY --from=tools /tmp/bin/ /bin @@ -128,7 +128,7 @@ ENTRYPOINT ["./retina/controller"] # agent final image for windows -FROM ${OS_VERSION} as agent-win +FROM ${OS_VERSION} AS agent-win COPY --from=controller-bin /go/src/github.com/microsoft/retina/windows/kubeconfigtemplate.yaml kubeconfigtemplate.yaml COPY --from=controller-bin /go/src/github.com/microsoft/retina/windows/setkubeconfigpath.ps1 setkubeconfigpath.ps1 COPY --from=controller-bin /go/bin/retina/controller controller.exe diff --git a/controller/Dockerfile.windows-2019 b/controller/Dockerfile.windows-2019 index a7edc1e123..b2385c38bd 100644 --- a/controller/Dockerfile.windows-2019 +++ b/controller/Dockerfile.windows-2019 @@ -1,4 +1,4 @@ -FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.21 as builder +FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.21 AS builder # Build args ARG VERSION ARG APP_INSIGHTS_ID diff --git a/controller/Dockerfile.windows-2022 b/controller/Dockerfile.windows-2022 index 686623762d..7a9ef50ff5 100644 --- a/controller/Dockerfile.windows-2022 +++ b/controller/Dockerfile.windows-2022 @@ -1,4 +1,6 @@ -FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.21 as builder +FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.21 AS builder + +# Build args ARG VERSION ARG APP_INSIGHTS_ID diff --git a/controller/Dockerfile.windows-cgo b/controller/Dockerfile.windows-cgo index b58e5208a2..e86a984e94 100644 --- a/controller/Dockerfile.windows-cgo +++ b/controller/Dockerfile.windows-cgo @@ -1,4 +1,4 @@ -FROM --platform=windows/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.22.2-windowsservercore-ltsc2022 as cgo +FROM --platform=windows/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.22.2-windowsservercore-ltsc2022 AS cgo SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] diff --git a/controller/Dockerfile.windows-native b/controller/Dockerfile.windows-native index 15e832b28f..df5c2fced9 100644 --- a/controller/Dockerfile.windows-native +++ b/controller/Dockerfile.windows-native @@ -3,7 +3,7 @@ # buildx targets, and this one requires legacy build. # Maybe one day: https://github.com/moby/buildkit/issues/616 ARG BUILDER_IMAGE -FROM --platform=windows/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.22-windowsservercore-ltsc2022 as builder +FROM --platform=windows/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.22-windowsservercore-ltsc2022 AS builder WORKDIR C:\\retina COPY go.mod . COPY go.sum . @@ -22,7 +22,7 @@ RUN go build -v -o captureworkload.exe -ldflags="-X github.com/microsoft/retina/ FROM --platform=windows/amd64 ${BUILDER_IMAGE} as pktmon-builder WORKDIR C:\\retina -FROM --platform=windows/amd64 mcr.microsoft.com/windows/nanoserver:ltsc2022 as final +FROM --platform=windows/amd64 mcr.microsoft.com/windows/nanoserver:ltsc2022 AS final ADD https://github.com/microsoft/etl2pcapng/releases/download/v1.10.0/etl2pcapng.exe /etl2pcapng.exe SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'Continue';"] COPY --from=builder C:\\retina\\controller.exe controller.exe diff --git a/deploy/hubble/manifests/controller/helm/retina/templates/agent/configmap.yaml b/deploy/hubble/manifests/controller/helm/retina/templates/agent/configmap.yaml index beea5ba49d..69c05fad1c 100644 --- a/deploy/hubble/manifests/controller/helm/retina/templates/agent/configmap.yaml +++ b/deploy/hubble/manifests/controller/helm/retina/templates/agent/configmap.yaml @@ -110,6 +110,7 @@ data: remoteContext: {{ .Values.remoteContext }} enableAnnotations: {{ .Values.enableAnnotations }} bypassLookupIPOfInterest: {{ .Values.bypassLookupIPOfInterest }} + dataAggregationLevel: {{ .Values.dataAggregationLevel }} {{- end}} --- {{- if .Values.os.windows}} diff --git a/deploy/hubble/manifests/controller/helm/retina/values.yaml b/deploy/hubble/manifests/controller/helm/retina/values.yaml index 5a01a753ed..56c3bf6a78 100644 --- a/deploy/hubble/manifests/controller/helm/retina/values.yaml +++ b/deploy/hubble/manifests/controller/helm/retina/values.yaml @@ -60,6 +60,7 @@ enablePodLevel: true remoteContext: false enableAnnotations: false bypassLookupIPOfInterest: true +dataAggregationLevel: "high" imagePullSecrets: [] nameOverride: "retina" @@ -178,7 +179,7 @@ hubble: # Possible values are: # 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, # 2047, 4095, 8191, 16383, 32767, 65535 - # eventBufferCapacity: "4095" + eventBufferCapacity: "4095" # -- Hubble metrics configuration. # See https://docs.cilium.io/en/stable/observability/metrics/#hubble-metrics diff --git a/deploy/legacy/manifests/controller/helm/retina/templates/configmap.yaml b/deploy/legacy/manifests/controller/helm/retina/templates/configmap.yaml index b974d6a348..1e98748d73 100644 --- a/deploy/legacy/manifests/controller/helm/retina/templates/configmap.yaml +++ b/deploy/legacy/manifests/controller/helm/retina/templates/configmap.yaml @@ -20,6 +20,8 @@ data: enablePodLevel: {{ .Values.enablePodLevel }} remoteContext: {{ .Values.remoteContext }} enableAnnotations: {{ .Values.enableAnnotations }} + bypassLookupIPOfInterest: {{ .Values.bypassLookupIPOfInterest }} + dataAggregationLevel: {{ .Values.dataAggregationLevel }} {{- end}} --- {{- if .Values.os.windows}} diff --git a/deploy/legacy/manifests/controller/helm/retina/values.yaml b/deploy/legacy/manifests/controller/helm/retina/values.yaml index 19174e56c2..c7ba9cd32b 100644 --- a/deploy/legacy/manifests/controller/helm/retina/values.yaml +++ b/deploy/legacy/manifests/controller/helm/retina/values.yaml @@ -41,6 +41,7 @@ enablePodLevel: false remoteContext: false enableAnnotations: false bypassLookupIPOfInterest: false +dataAggregationLevel: "low" imagePullSecrets: [] nameOverride: "retina" diff --git a/docs/concepts/dataAggregation.md b/docs/concepts/dataAggregation.md new file mode 100644 index 0000000000..12147a67a4 --- /dev/null +++ b/docs/concepts/dataAggregation.md @@ -0,0 +1,7 @@ +# Data Aggregation + +Under Retina's hood, data are communicate between plugins and the control plane via [`Flow` objects](https://github.com/cilium/cilium/tree/main/api/v1/flow). Retina's data aggregation settings are designed to manage the amount of data that can be potentially generate by the agent .i.e the number of `flows` being generated. At a higher aggregation level, fewer `flows` objects are produced, which ensures resource efficiency in large clusters. Conversely, a lower level of aggregation results in more `flow` objects being generated, offering more detailed information regarding packets being observed at different points in the Linux kernel.The operational behaviors of Retina at each aggregation level are detailed in the table below: +| Level | Description| +|--- |--- | +| `low` | `packetparser` will attach a bpf program to the node's default interface in the node namespace, which will help capture metrics for `TO_NETWORK` and `FROM_NETWORK` packets. This will give users a more granular view of packet flows and offers more reliable apiserver latency metrics. | +| `high` | `packetparser` will not attach a bpf program to the node's default interface in the node namespace. As a result, packet observation at this location will be disabled, leading to a reduction in metrics being generated. This configuration is recommended when scalability is the primary concern. However, it is important to note that, due to the absence of packet observation at the default interface, the apiserver latency metrics may not be as reliable. | diff --git a/docs/installation/config.md b/docs/installation/config.md index 54d74ed7b4..ffd140df0b 100644 --- a/docs/installation/config.md +++ b/docs/installation/config.md @@ -13,6 +13,7 @@ Defaults are specified for each component in *deploy/legacy/manifests/controller * `enabledPlugin_linux`: Array of enabled plugins for linux. * `enabledPlugin_win`: Array of enabled plugins for windows. * `metricsInterval`: the interval for which metrics will be gathered. +* `dataAggregationLevel`: This config defines the level of data aggregation for Retina. See [Data Aggregation](../concepts/dataAggregation.md) for more details. ## Operator Config diff --git a/operator/Dockerfile b/operator/Dockerfile index 98423adec7..1aa9477f4c 100644 --- a/operator/Dockerfile +++ b/operator/Dockerfile @@ -21,7 +21,7 @@ RUN --mount=type=cache,target="/root/.cache/go-build" \ -a -o retina-operator operator/main.go ##################### controller ####################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/mirror/gcr/distroless/cc-debian11:latest@sha256:b53fbf5f81f4a120a489fedff2092e6fcbeacf7863fce3e45d99cc58dc230ccc as controller +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/mirror/gcr/distroless/cc-debian11:latest@sha256:b53fbf5f81f4a120a489fedff2092e6fcbeacf7863fce3e45d99cc58dc230ccc AS controller WORKDIR / COPY --from=builder /workspace/retina-operator . diff --git a/operator/Dockerfile.windows-2019 b/operator/Dockerfile.windows-2019 index cde7bf6a48..d097abf015 100644 --- a/operator/Dockerfile.windows-2019 +++ b/operator/Dockerfile.windows-2019 @@ -1,4 +1,4 @@ -FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.22 as builder +FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.22 AS builder # Build args ARG VERSION diff --git a/operator/Dockerfile.windows-2022 b/operator/Dockerfile.windows-2022 index e0073a0402..4e5c215c2d 100644 --- a/operator/Dockerfile.windows-2022 +++ b/operator/Dockerfile.windows-2022 @@ -1,4 +1,4 @@ -FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.22 as builder +FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.22 AS builder # Build args ARG VERSION diff --git a/pkg/config/config.go b/pkg/config/config.go index fa06341ff2..5ca53e8de0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -4,18 +4,54 @@ package config import ( "fmt" + "reflect" + "strings" "time" + "github.com/mitchellh/mapstructure" "github.com/spf13/viper" ) +// Level defines the level of monitor aggregation. +type Level int + +const ( + Low Level = iota + High +) + +func (l *Level) UnmarshalText(text []byte) error { + s := strings.ToLower(string(text)) + switch s { + case "low": + *l = Low + case "high": + *l = High + default: + // Default to Low if the text is not recognized. + *l = Low + } + return nil +} + +func (l *Level) String() string { + switch *l { + case Low: + return "low" + case High: + return "high" + default: + return "" + } +} + type Server struct { Host string `yaml:"host"` Port int `yaml:"port"` } type Config struct { - ApiServer Server `yaml:"apiServer"` + APIServer Server `yaml:"apiServer"` LogLevel string `yaml:"logLevel"` EnabledPlugin []string `yaml:"enabledPlugin"` MetricsInterval time.Duration `yaml:"metricsInterval"` @@ -25,6 +61,7 @@ type Config struct { RemoteContext bool `yaml:"remoteContext"` EnableAnnotations bool `yaml:"enableAnnotations"` BypassLookupIPOfInterest bool `yaml:"bypassLookupIPOfInterest"` + DataAggregationLevel Level `yaml:"dataAggregationLevel"` } func GetConfig(cfgFilename string) (*Config, error) { @@ -46,7 +83,14 @@ func GetConfig(cfgFilename string) (*Config, error) { return nil, fmt.Errorf("fatal error config file: %s", err) } var config Config - err = viper.Unmarshal(&config) + decoderConfigOption := func(dc *mapstructure.DecoderConfig) { + dc.DecodeHook = mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), // default hook. + mapstructure.StringToSliceHookFunc(","), // default hook. + decodeLevelHook, + ) + } + err = viper.Unmarshal(&config, decoderConfigOption) if err != nil { return nil, fmt.Errorf("fatal error config file: %s", err) } @@ -55,3 +99,20 @@ func GetConfig(cfgFilename string) (*Config, error) { return &config, nil } + +func decodeLevelHook(field, target reflect.Type, data interface{}) (interface{}, error) { + // Check if the field we are decoding is a string. + if field.Kind() != reflect.String { + return data, nil + } + // Check if the type we are decoding to is a Level. + if target != reflect.TypeOf(Level(0)) { + return data, nil + } + var level Level + err := level.UnmarshalText([]byte(data.(string))) + if err != nil { + return nil, err + } + return level, nil +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 25b0d38bb0..203be5912b 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -4,8 +4,12 @@ package config import ( + "reflect" "testing" "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestGetConfig(t *testing.T) { @@ -13,15 +17,35 @@ func TestGetConfig(t *testing.T) { if err != nil { t.Fatalf("Expected no error, instead got %+v", err) } - if c.ApiServer.Host != "0.0.0.0" || - c.ApiServer.Port != 10093 || + if c.APIServer.Host != "0.0.0.0" || + c.APIServer.Port != 10093 || c.LogLevel != "info" || c.MetricsInterval != 10*time.Second || len(c.EnabledPlugin) != 3 || c.EnablePodLevel || !c.EnableRetinaEndpoint || c.RemoteContext || - c.EnableAnnotations { + c.EnableAnnotations || + c.DataAggregationLevel != Low { t.Fatalf("Expeted config should be same as ./testwith/config.yaml; instead got %+v", c) } } + +func TestDecodeLevelHook(t *testing.T) { + tests := []struct { + input interface{} + expected interface{} + }{ + {"low", Low}, + {"high", High}, + {"invalid", Low}, // Unimplemented or invalid input should default to Low + {123, 123}, // Non-string input should be returned as is + } + + for _, test := range tests { + result, err := decodeLevelHook(reflect.TypeOf(test.input), reflect.TypeOf(Level(0)), test.input) + require.NoError(t, err) + assert.Equal(t, test.expected, result) + + } +} diff --git a/pkg/config/hubble_config_linux.go b/pkg/config/hubble_config_linux.go index 8f558c0bdb..e092f54937 100644 --- a/pkg/config/hubble_config_linux.go +++ b/pkg/config/hubble_config_linux.go @@ -50,6 +50,7 @@ var ( EnablePodLevel: true, LogLevel: "info", BypassLookupIPOfInterest: true, + DataAggregationLevel: High, } Cell = cell.Module( diff --git a/pkg/config/testwith/config.yaml b/pkg/config/testwith/config.yaml index 8c55954d58..ab0494a943 100644 --- a/pkg/config/testwith/config.yaml +++ b/pkg/config/testwith/config.yaml @@ -8,3 +8,4 @@ enabledPlugin: ["dropreason", "packetforward", "linuxutil"] metricsInterval: 10 # used to export telemetry to AppInsights telemetryEnabled: true +dataAggregationLevel: "low" diff --git a/pkg/managers/controllermanager/controllermanager.go b/pkg/managers/controllermanager/controllermanager.go index f8464cc972..8ca2ddf17f 100644 --- a/pkg/managers/controllermanager/controllermanager.go +++ b/pkg/managers/controllermanager/controllermanager.go @@ -62,8 +62,8 @@ func NewControllerManager(conf *kcfg.Config, kubeclient kubernetes.Interface, te // create HTTP server for API server httpServer := sm.NewHTTPServer( - conf.ApiServer.Host, - conf.ApiServer.Port, + conf.APIServer.Host, + conf.APIServer.Port, ) return &Controller{ diff --git a/pkg/plugin/dropreason/dropreason_linux_test.go b/pkg/plugin/dropreason/dropreason_linux_test.go index e6887a57d7..e8641cc3b7 100644 --- a/pkg/plugin/dropreason/dropreason_linux_test.go +++ b/pkg/plugin/dropreason/dropreason_linux_test.go @@ -30,6 +30,7 @@ var ( MetricsInterval: 1 * time.Second, EnablePodLevel: true, BypassLookupIPOfInterest: true, + DataAggregationLevel: kcfg.Low, } cfgPodLevelDisabled = &kcfg.Config{ MetricsInterval: 1 * time.Second, diff --git a/pkg/plugin/packetparser/packetparser_linux.go b/pkg/plugin/packetparser/packetparser_linux.go index c57f065588..fe8132ba77 100644 --- a/pkg/plugin/packetparser/packetparser_linux.go +++ b/pkg/plugin/packetparser/packetparser_linux.go @@ -8,13 +8,14 @@ import ( "bytes" "context" "encoding/binary" - "errors" "fmt" "os" "path" "runtime" "sync" + "github.com/pkg/errors" + "github.com/cilium/cilium/api/v1/flow" v1 "github.com/cilium/cilium/pkg/hubble/api/v1" "github.com/cilium/ebpf" @@ -207,22 +208,27 @@ func (p *packetParser) Start(ctx context.Context) error { p.callbackID = ps.Subscribe(common.PubSubEndpoints, &fn) } - outgoingLinks, err := utils.GetDefaultOutgoingLinks() - if err != nil { - return err - } - if len(outgoingLinks) == 0 { - return errNoOutgoingLinks + if p.cfg.DataAggregationLevel == kcfg.Low { + p.l.Info("Attaching bpf program to default interface of k8s Node in node namespace") + outgoingLinks, err := utils.GetDefaultOutgoingLinks() + if err != nil { + return errors.Wrap(err, "could not get default outgoing links") + } + if len(outgoingLinks) == 0 { + return errNoOutgoingLinks + } + outgoingLink := outgoingLinks[0] // Take first link until multi-link support is implemented + + outgoingLinkAttributes := outgoingLink.Attrs() + p.l.Info("Attaching Packetparser", + zap.Int("outgoingLink.Index", outgoingLinkAttributes.Index), + zap.String("outgoingLink.Name", outgoingLinkAttributes.Name), + zap.Stringer("outgoingLink.HardwareAddr", outgoingLinkAttributes.HardwareAddr), + ) + p.createQdiscAndAttach(*outgoingLink.Attrs(), Device) + } else { + p.l.Info("Skipping attaching bpf program to default interface of k8s Node in node namespace") } - outgoingLink := outgoingLinks[0] // Take first link until multi-link support is implemented - - outgoingLinkAttributes := outgoingLink.Attrs() - p.l.Info("Attaching Packetparser", - zap.Int("outgoingLink.Index", outgoingLinkAttributes.Index), - zap.String("outgoingLink.Name", outgoingLinkAttributes.Name), - zap.Stringer("outgoingLink.HardwareAddr", outgoingLinkAttributes.HardwareAddr), - ) - p.createQdiscAndAttach(*outgoingLink.Attrs(), Device) // Create the channel. p.recordsChannel = make(chan perf.Record, buffer) diff --git a/pkg/plugin/packetparser/packetparser_linux_test.go b/pkg/plugin/packetparser/packetparser_linux_test.go index b15e0f5406..630a8e8eb2 100644 --- a/pkg/plugin/packetparser/packetparser_linux_test.go +++ b/pkg/plugin/packetparser/packetparser_linux_test.go @@ -40,6 +40,14 @@ var ( cfgPodLevelDisabled = &kcfg.Config{ EnablePodLevel: false, } + cfgDataAggregationLevelLow = &kcfg.Config{ + EnablePodLevel: true, + DataAggregationLevel: kcfg.Low, + } + cfgDataAggregationLevelHigh = &kcfg.Config{ + EnablePodLevel: true, + DataAggregationLevel: kcfg.High, + } ) func TestCleanAll(t *testing.T) { @@ -358,6 +366,132 @@ func TestStartPodLevelDisabled(t *testing.T) { require.NoError(t, err) } +func TestStartWithDataAggregationLevelLow(t *testing.T) { + log.SetupZapLogger(log.GetDefaultLogOpts()) // nolint:errcheck // ignore + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockFilter := mocks.NewMockIFilter(ctrl) + mQdisc := mocks.NewMockIQdisc(ctrl) + + // We are expecting two calls to Add since we are invoking createQdiscAndAttach for eth0 + mockFilter.EXPECT().Add(gomock.Any()).Return(nil).Times(2) + mQdisc.EXPECT().Add(gomock.Any()).Return(nil).Times(2) + + mockTC := mocks.NewMockITc(ctrl) + + mockReader := mocks.NewMockIPerf(ctrl) + mockReader.EXPECT().Read().Return(perf.Record{}, nil).AnyTimes() + + getQdisc = func(_ ITc) IQdisc { + return mQdisc + } + + getFilter = func(_ ITc) IFilter { + return mockFilter + } + + tcOpen = func(_ *tc.Config) (ITc, error) { + return mockTC, nil + } + + getFD = func(_ *ebpf.Program) int { + return 1 + } + + pObj := &packetparserObjects{} + pObj.EndpointIngressFilter = &ebpf.Program{} + pObj.EndpointEgressFilter = &ebpf.Program{} + + p := &packetParser{ + cfg: cfgDataAggregationLevelLow, + l: log.Logger().Named("test"), + objs: pObj, + reader: mockReader, + interfaceLockMap: &sync.Map{}, + endpointIngressInfo: &ebpf.ProgramInfo{ + Name: "ingress", + }, + endpointEgressInfo: &ebpf.ProgramInfo{ + Name: "egress", + }, + hostIngressInfo: &ebpf.ProgramInfo{ + Name: "ingress", + }, + hostEgressInfo: &ebpf.ProgramInfo{ + Name: "egress", + }, + tcMap: &sync.Map{}, + } + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + err := p.Start(ctx) + require.NoError(t, err) +} + +func TestStartWithDataAggregationLevelHigh(t *testing.T) { + log.SetupZapLogger(log.GetDefaultLogOpts()) // nolint:errcheck // ignore + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockFilter := mocks.NewMockIFilter(ctrl) + mQdisc := mocks.NewMockIQdisc(ctrl) + + // We are not expecting any calls to Add since we are not invoking createQdiscAndAttach for eth0 + mockFilter.EXPECT().Add(gomock.Any()).Return(nil).Times(0) + mQdisc.EXPECT().Add(gomock.Any()).Return(nil).Times(0) + + mockTC := mocks.NewMockITc(ctrl) + + mockReader := mocks.NewMockIPerf(ctrl) + mockReader.EXPECT().Read().Return(perf.Record{}, nil).AnyTimes() + + getQdisc = func(_ ITc) IQdisc { + return mQdisc + } + + getFilter = func(_ ITc) IFilter { + return mockFilter + } + + tcOpen = func(_ *tc.Config) (ITc, error) { + return mockTC, nil + } + + getFD = func(_ *ebpf.Program) int { + return 1 + } + + pObj := &packetparserObjects{} + pObj.EndpointIngressFilter = &ebpf.Program{} + pObj.EndpointEgressFilter = &ebpf.Program{} + + p := &packetParser{ + cfg: cfgDataAggregationLevelHigh, + l: log.Logger().Named("test"), + objs: pObj, + reader: mockReader, + interfaceLockMap: &sync.Map{}, + endpointIngressInfo: &ebpf.ProgramInfo{ + Name: "ingress", + }, + endpointEgressInfo: &ebpf.ProgramInfo{ + Name: "egress", + }, + hostIngressInfo: &ebpf.ProgramInfo{ + Name: "ingress", + }, + hostEgressInfo: &ebpf.ProgramInfo{ + Name: "egress", + }, + tcMap: &sync.Map{}, + } + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + err := p.Start(ctx) + require.NoError(t, err) +} + func TestInitPodLevelDisabled(t *testing.T) { log.SetupZapLogger(log.GetDefaultLogOpts()) p := &packetParser{ diff --git a/pkg/servermanager/cell_linux.go b/pkg/servermanager/cell_linux.go index b0315c54e8..a7b131867e 100644 --- a/pkg/servermanager/cell_linux.go +++ b/pkg/servermanager/cell_linux.go @@ -29,7 +29,7 @@ func newServerManager(params serverParams) (*sm.HTTPServer, error) { logger := params.Log.WithField("module", "servermanager") serverCtx, cancelCtx := context.WithCancel(context.Background()) - serverManager := sm.NewHTTPServer(params.Config.ApiServer.Host, params.Config.ApiServer.Port) + serverManager := sm.NewHTTPServer(params.Config.APIServer.Host, params.Config.APIServer.Port) if err := serverManager.Init(); err != nil { logger.WithError(err).Error("Unable to initialize Http server") cancelCtx()