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

fix: obsy tests #578

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 90 additions & 24 deletions e2e/basic/observability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"io"
"net/http"
"regexp"
"strings"
"time"

"github.com/celestiaorg/knuu/pkg/sidecars/observability"
Expand All @@ -14,19 +16,26 @@ const (
prometheusPort = observability.DefaultOtelMetricsPort
prometheusImage = "prom/prometheus:latest"
prometheusConfig = "/etc/prometheus/prometheus.yml"
prometheusArgs = "--config.file=/etc/prometheus/prometheus.yml"
prometheusArgs = "--config.file=" + prometheusConfig

curlImage = "curlimages/curl:latest"
otlpPort = observability.DefaultOtelOtlpPort

retryInterval = 1 * time.Second
retryTimeout = 10 * time.Second
)

// TestObservabilityCollector is a test function that verifies the functionality of the otel collector setup
func (s *Suite) TestObservabilityCollector() {
const (
namePrefix = "observability"
targetStartCommand = "while true; do curl -X POST http://localhost:8888/v1/traces; sleep 5; done"
namePrefix = "observability"
scrapeInterval = "2s"
prometheusQueryTimeout = 30 * time.Second
)
var (
targetStartCommand = fmt.Sprintf("while true; do curl -X POST http://localhost:%d/v1/traces; sleep 2; done", otlpPort)
ctx = context.Background()
)
ctx := context.Background()

// Setup Prometheus
prometheus, err := s.Knuu.NewInstance(namePrefix + "-prometheus")
Expand All @@ -44,12 +53,12 @@ func (s *Suite) TestObservabilityCollector() {
// Add Prometheus config file
prometheusConfigContent := fmt.Sprintf(`
global:
scrape_interval: '10s'
scrape_interval: '%s'

scrape_configs:
- job_name: 'otel-collector'
static_configs:
- targets: ['otel-collector:%d']
`, otlpPort)
- targets: ['otel-collector:%d']`, scrapeInterval, otlpPort)
s.Require().NoError(prometheus.Storage().AddFileBytes([]byte(prometheusConfigContent), prometheusConfig, "0:0"))

s.Require().NoError(prometheus.Build().SetArgs(prometheusArgs))
Expand All @@ -59,12 +68,12 @@ scrape_configs:
observabilitySidecar := observability.New()

s.Require().NoError(observabilitySidecar.SetOtelEndpoint(4318))
s.Require().NoError(observabilitySidecar.SetPrometheusEndpoint(otlpPort, fmt.Sprintf("knuu-%s", s.Knuu.Scope), "10s"))
s.Require().NoError(observabilitySidecar.SetPrometheusEndpoint(otlpPort, fmt.Sprintf("knuu-%s", s.Knuu.Scope), scrapeInterval))
s.Require().NoError(observabilitySidecar.SetJaegerEndpoint(14250, 6831, 14268))
s.Require().NoError(observabilitySidecar.SetOtlpExporter("prometheus:9090", "", ""))

// Create and start a target pod and configure it to use the obsySidecar to push metrics
target, err := s.Knuu.NewInstance(namePrefix + "target")
target, err := s.Knuu.NewInstance(namePrefix + "-target")
s.Require().NoError(err)

s.Require().NoError(target.Build().SetImage(ctx, curlImage))
Expand All @@ -73,30 +82,87 @@ scrape_configs:
s.Require().NoError(err)

s.Require().NoError(target.Sidecars().Add(ctx, observabilitySidecar))

s.Require().NoError(target.Build().Commit(ctx))
s.Require().NoError(target.Execution().Start(ctx))

// Wait for the target pod to push data to the otel collector
s.T().Log("Waiting one minute for the target pod to push data to the otel collector")
time.Sleep(1 * time.Minute)

// Verify that data has been pushed to Prometheus
s.Require().Eventually(func() bool {
url := fmt.Sprintf("%s/api/v1/query?query=up", prometheusEndpoint)
ctx, cancel := context.WithTimeout(context.Background(), prometheusQueryTimeout)
defer cancel()

req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
s.T().Logf("Error creating request: %v", err)
return false
}

resp, err := http.DefaultClient.Do(req)
if err != nil {
s.T().Logf("Error sending request: %v", err)
return false
}
if resp.StatusCode != http.StatusOK {
s.T().Logf("Prometheus API returned status code: %d", resp.StatusCode)
return false
}

defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
s.T().Logf("Error reading response body: %v", err)
return false
}
return strings.Contains(string(body), "otel-collector")

}, retryTimeout, retryInterval, "otel-collector data source not found in Prometheus")
}

prometheusURL := fmt.Sprintf("%s/api/v1/query?query=up", prometheusEndpoint)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
func (s *Suite) TestObservabilityCollectorWithLogging() {
const (
namePrefix = "observability"
targetStartCommand = "while true; do curl -X POST http://localhost:8888/v1/traces; sleep 2; done"
)
ctx := context.Background()

req, err := http.NewRequestWithContext(ctx, "GET", prometheusURL, nil)
s.Require().NoError(err)
// Setup obsySidecar collector
obsySidecar := observability.New()

s.Require().NoError(obsySidecar.SetOtelEndpoint(4318))
s.Require().NoError(obsySidecar.SetLoggingExporter("debug"))

resp, err := http.DefaultClient.Do(req)
// Create and start a target pod and configure it to use the obsySidecar to push metrics
target, err := s.Knuu.NewInstance(namePrefix + "target")
s.Require().NoError(err)
s.Require().Equal(http.StatusOK, resp.StatusCode, "Prometheus API is not accessible")

defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
s.Require().NoError(target.Build().SetImage(ctx, curlImage))

err = target.Build().SetStartCommand("sh", "-c", targetStartCommand)
s.Require().NoError(err)
s.Require().Contains(string(body), "otel-collector", "otel-collector data source not found in Prometheus")

s.T().Log("otel-collector data source is available in Prometheus")
s.Require().NoError(target.Sidecars().Add(ctx, obsySidecar))
s.Require().NoError(target.Build().Commit(ctx))
s.Require().NoError(target.Execution().Start(ctx))

// Verify that data has been pushed to the logging exporter
s.Require().Eventually(func() bool {
logsReader, err := obsySidecar.Instance().Monitoring().Logs(ctx)
if err != nil {
s.T().Logf("Error getting logs: %v", err)
return false
}
logsOutput, err := io.ReadAll(logsReader)
if err != nil {
s.T().Logf("Error reading logs: %v", err)
return false
}

loggingExporterPattern := regexp.MustCompile(`"kind": "exporter", "data_type": "metrics", "name": "logging"`)
if !loggingExporterPattern.Match(logsOutput) {
s.T().Logf("Logging exporter not found in the logs: `%s`", string(logsOutput))
return false
}
return true
}, retryTimeout, retryInterval, "Logging exporter not found in the logs")
}
2 changes: 1 addition & 1 deletion e2e/system/build_image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (s *Suite) TestBuildWithBuildArgs() {
// This file is created by the dockerfile in the repo
// ref: https://github.com/celestiaorg/knuu/blob/test/build-from-git/Dockerfile
filePath = "/test.txt"
expectedData = "Hello, build arg!"
expectedData = "Hello, World!"
)

s.T().Log("Creating new instance")
Expand Down
13 changes: 13 additions & 0 deletions pkg/sidecars/observability/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@ func (o *Obsy) SetPrometheusRemoteWriteExporter(endpoint string) error {
return nil
}

// SetLoggingExporter sets the logging exporter for the instance
func (o *Obsy) SetLoggingExporter(logLevel string) error {
if err := o.validateStateForObsy("Logging exporter"); err != nil {
return err
}

if logLevel == "" {
logLevel = defaultLoggingExporterLogLevel
}
o.obsyConfig.loggingExporterLogLevel = logLevel
return nil
}

func (o *Obsy) validateStateForObsy(endpoint string) error {
if o.instance != nil && !o.instance.IsInState(instance.StateNone) {
return ErrSettingNotAllowed.WithParams(endpoint, o.instance.State().String())
Expand Down
3 changes: 3 additions & 0 deletions pkg/sidecars/observability/obsy.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ type ObsyConfig struct {

// prometheusRemoteWriteExporterEndpoint is the endpoint of the prometheus remote write
prometheusRemoteWriteExporterEndpoint string

// loggingExporterLogLevel is the log level for the logging exporter
loggingExporterLogLevel string
}

func New() *Obsy {
Expand Down
35 changes: 35 additions & 0 deletions pkg/sidecars/observability/otel.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const (
jaegerExporterName = "jaeger"
prometheusExporterName = "prometheus"
prometheusRemoteWriteExporterName = "prometheusremotewrite"
loggingExporterName = "logging"
defaultLoggingExporterLogLevel = "debug"
attributesProcessorName = "attributes"

scopeAttributeKey = "scope"
Expand Down Expand Up @@ -112,6 +114,7 @@ type Exporters struct {
Jaeger JaegerExporter `yaml:"jaeger,omitempty"`
Prometheus PrometheusExporter `yaml:"prometheus,omitempty"`
PrometheusRemoteWrite PrometheusRemoteWriteExporter `yaml:"prometheusremotewrite,omitempty"`
Logging LoggingExporter `yaml:"logging,omitempty"`
}

type OTLPHTTPExporter struct {
Expand All @@ -137,6 +140,10 @@ type PrometheusRemoteWriteExporter struct {
TLS TLS `yaml:"tls,omitempty"`
}

type LoggingExporter struct {
LogLevel string `yaml:"loglevel,omitempty"`
}

type TLS struct {
Insecure bool `yaml:"insecure,omitempty"`
}
Expand Down Expand Up @@ -319,6 +326,12 @@ func (o *Obsy) createPrometheusRemoteWriteExporter() PrometheusRemoteWriteExport
}
}

func (o *Obsy) createLoggingExporter() LoggingExporter {
return LoggingExporter{
LogLevel: o.obsyConfig.loggingExporterLogLevel,
}
}

func (o *Obsy) createExporters() Exporters {
exporters := Exporters{}

Expand All @@ -338,6 +351,10 @@ func (o *Obsy) createExporters() Exporters {
exporters.PrometheusRemoteWrite = o.createPrometheusRemoteWriteExporter()
}

if o.obsyConfig.loggingExporterLogLevel != "" {
exporters.Logging = o.createLoggingExporter()
}

return exporters
}

Expand All @@ -358,7 +375,16 @@ func (o *Obsy) prepareMetricsForServicePipeline() Metrics {
if o.obsyConfig.prometheusRemoteWriteExporterEndpoint != "" {
metrics.Exporters = append(metrics.Exporters, prometheusRemoteWriteExporterName)
}
if o.obsyConfig.loggingExporterLogLevel != "" {
metrics.Exporters = append(metrics.Exporters, loggingExporterName)
}
metrics.Processors = []string{attributesProcessorName}

// if no metrics receiver or exporter is added, remove any metrics pipeline
if len(metrics.Receivers) == 0 || len(metrics.Exporters) == 0 {
metrics = Metrics{}
}

return metrics
}

Expand All @@ -376,7 +402,16 @@ func (o *Obsy) prepareTracesForServicePipeline() Traces {
if o.obsyConfig.jaegerEndpoint != "" {
traces.Exporters = append(traces.Exporters, jaegerExporterName)
}
if o.obsyConfig.loggingExporterLogLevel != "" {
traces.Exporters = append(traces.Exporters, loggingExporterName)
}
traces.Processors = []string{attributesProcessorName}

// if no trace receiver or exporter is added, remove any trace pipeline
if len(traces.Receivers) == 0 || len(traces.Exporters) == 0 {
traces = Traces{}
}

return traces
}

Expand Down
Loading