Skip to content

Commit

Permalink
Add custom healthcheckextension (#400)
Browse files Browse the repository at this point in the history
Fixes #395


All of the code is copied except the handler code in func `baseHandler`
and `Handler()` is modified to return success all the time under the
extension name `signoz_health_check`.
  • Loading branch information
srikanthccv authored Sep 20, 2024
1 parent 629ff58 commit 1712bfa
Show file tree
Hide file tree
Showing 17 changed files with 974 additions and 4 deletions.
4 changes: 0 additions & 4 deletions .github/CODEOWNERS

This file was deleted.

2 changes: 2 additions & 0 deletions components/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ import (
"github.com/SigNoz/signoz-otel-collector/exporter/clickhousemetricsexporter"
"github.com/SigNoz/signoz-otel-collector/exporter/clickhousetracesexporter"
"github.com/SigNoz/signoz-otel-collector/exporter/signozkafkaexporter"
signozhealthcheckextension "github.com/SigNoz/signoz-otel-collector/extension/healthcheckextension"
_ "github.com/SigNoz/signoz-otel-collector/pkg/parser/grok"
"github.com/SigNoz/signoz-otel-collector/processor/signozlogspipelineprocessor"
"github.com/SigNoz/signoz-otel-collector/processor/signozspanmetricsprocessor"
Expand Down Expand Up @@ -304,6 +305,7 @@ func CoreComponents() (
oidcauthextension.NewFactory(),
healthcheckextension.NewFactory(),
pprofextension.NewFactory(),
signozhealthcheckextension.NewFactory(),
zpagesextension.NewFactory(),
)
errs = multierr.Append(errs, err)
Expand Down
2 changes: 2 additions & 0 deletions extension/healthcheckextension/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include ../../Makefile.Common

3 changes: 3 additions & 0 deletions extension/healthcheckextension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Health Check

This is a copy of upstream health check that always returns healthy.
74 changes: 74 additions & 0 deletions extension/healthcheckextension/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package healthcheckextension // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension"

import (
"errors"
"strings"
"time"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/confighttp"
)

type ResponseBodySettings struct {
// Healthy represents the body of the response returned when the collector is healthy.
// The default value is ""
Healthy string `mapstructure:"healthy"`

// Unhealthy represents the body of the response returned when the collector is unhealthy.
// The default value is ""
Unhealthy string `mapstructure:"unhealthy"`
}

// Config has the configuration for the extension enabling the health check
// extension, used to report the health status of the service.
type Config struct {
confighttp.ServerConfig `mapstructure:",squash"`

// Path represents the path the health check service will serve.
// The default path is "/".
Path string `mapstructure:"path"`

// ResponseBody represents the body of the response returned by the health check service.
// This overrides the default response that it would return.
ResponseBody *ResponseBodySettings `mapstructure:"response_body"`

// CheckCollectorPipeline contains the list of settings of collector pipeline health check
CheckCollectorPipeline checkCollectorPipelineSettings `mapstructure:"check_collector_pipeline"`
}

var _ component.Config = (*Config)(nil)
var (
errNoEndpointProvided = errors.New("bad config: endpoint must be specified")
errInvalidExporterFailureThresholdProvided = errors.New("bad config: exporter_failure_threshold expects a positive number")
errInvalidPath = errors.New("bad config: path must start with /")
)

// Validate checks if the extension configuration is valid
func (cfg *Config) Validate() error {
_, err := time.ParseDuration(cfg.CheckCollectorPipeline.Interval)
if err != nil {
return err
}
if cfg.Endpoint == "" {
return errNoEndpointProvided
}
if cfg.CheckCollectorPipeline.ExporterFailureThreshold <= 0 {
return errInvalidExporterFailureThresholdProvided
}
if !strings.HasPrefix(cfg.Path, "/") {
return errInvalidPath
}
return nil
}

type checkCollectorPipelineSettings struct {
// Enabled indicates whether to not enable collector pipeline check.
Enabled bool `mapstructure:"enabled"`
// Interval the time range to check healthy status of collector pipeline
Interval string `mapstructure:"interval"`
// ExporterFailureThreshold is the threshold of exporter failure numbers during the Interval
ExporterFailureThreshold int `mapstructure:"exporter_failure_threshold"`
}
80 changes: 80 additions & 0 deletions extension/healthcheckextension/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package healthcheckextension

import (
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/config/configtls"
"go.opentelemetry.io/collector/confmap/confmaptest"

"github.com/SigNoz/signoz-otel-collector/extension/healthcheckextension/internal/metadata"
)

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

tests := []struct {
id component.ID
expected component.Config
expectedErr error
}{
{
id: component.NewID(metadata.Type),
expected: NewFactory().CreateDefaultConfig(),
},
{
id: component.NewIDWithName(metadata.Type, "1"),
expected: &Config{
ServerConfig: confighttp.ServerConfig{
Endpoint: "localhost:13",
TLSSetting: &configtls.ServerConfig{
Config: configtls.Config{
CAFile: "/path/to/ca",
CertFile: "/path/to/cert",
KeyFile: "/path/to/key",
},
},
},
CheckCollectorPipeline: defaultCheckCollectorPipelineSettings(),
Path: "/",
ResponseBody: nil,
},
},
{
id: component.NewIDWithName(metadata.Type, "missingendpoint"),
expectedErr: errNoEndpointProvided,
},
{
id: component.NewIDWithName(metadata.Type, "invalidthreshold"),
expectedErr: errInvalidExporterFailureThresholdProvided,
},
{
id: component.NewIDWithName(metadata.Type, "invalidpath"),
expectedErr: errInvalidPath,
},
}
for _, tt := range tests {
t.Run(tt.id.String(), func(t *testing.T) {
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
require.NoError(t, err)
factory := NewFactory()
cfg := factory.CreateDefaultConfig()
sub, err := cm.Sub(tt.id.String())
require.NoError(t, err)
require.NoError(t, component.UnmarshalConfig(sub, cfg))
if tt.expectedErr != nil {
assert.ErrorIs(t, component.ValidateConfig(cfg), tt.expectedErr)
return
}
assert.NoError(t, component.ValidateConfig(cfg))
assert.Equal(t, tt.expected, cfg)
})
}
}
9 changes: 9 additions & 0 deletions extension/healthcheckextension/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

//go:generate mdatagen metadata.yaml

// Package healthcheckextension implements an extension that enables an HTTP
// endpoint that can be used to check the overall health and status of the
// service.
package healthcheckextension // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension"
52 changes: 52 additions & 0 deletions extension/healthcheckextension/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package healthcheckextension // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension"

import (
"context"
"fmt"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/extension"

"github.com/SigNoz/signoz-otel-collector/extension/healthcheckextension/internal/metadata"
)

const defaultPort = 13133

// NewFactory creates a factory for HealthCheck extension.
func NewFactory() extension.Factory {
return extension.NewFactory(
metadata.Type,
createDefaultConfig,
createExtension,
metadata.ExtensionStability,
)
}

func createDefaultConfig() component.Config {
return &Config{
ServerConfig: confighttp.ServerConfig{
Endpoint: fmt.Sprintf("0.0.0.0:%d", defaultPort),
},
CheckCollectorPipeline: defaultCheckCollectorPipelineSettings(),
Path: "/",
}
}

func createExtension(_ context.Context, set extension.CreateSettings, cfg component.Config) (extension.Extension, error) {
config := cfg.(*Config)

return newServer(*config, set.TelemetrySettings), nil
}

// defaultCheckCollectorPipelineSettings returns the default settings for CheckCollectorPipeline.
func defaultCheckCollectorPipelineSettings() checkCollectorPipelineSettings {
return checkCollectorPipelineSettings{
Enabled: false,
Interval: "5m",
ExporterFailureThreshold: 5,
}
}
42 changes: 42 additions & 0 deletions extension/healthcheckextension/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package healthcheckextension

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/extension/extensiontest"

"github.com/SigNoz/signoz-otel-collector/internal/common/testutil"
)

func TestFactory_CreateDefaultConfig(t *testing.T) {
cfg := createDefaultConfig()
assert.Equal(t, &Config{
ServerConfig: confighttp.ServerConfig{
Endpoint: "0.0.0.0:13133",
},
CheckCollectorPipeline: defaultCheckCollectorPipelineSettings(),
Path: "/",
}, cfg)

assert.NoError(t, componenttest.CheckConfigStruct(cfg))
ext, err := createExtension(context.Background(), extensiontest.NewNopCreateSettings(), cfg)
require.NoError(t, err)
require.NotNil(t, ext)
}

func TestFactory_CreateExtension(t *testing.T) {
cfg := createDefaultConfig().(*Config)
cfg.Endpoint = testutil.GetAvailableLocalAddress(t)

ext, err := createExtension(context.Background(), extensiontest.NewNopCreateSettings(), cfg)
require.NoError(t, err)
require.NotNil(t, ext)
}
13 changes: 13 additions & 0 deletions extension/healthcheckextension/generated_package_test.go

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

Loading

0 comments on commit 1712bfa

Please sign in to comment.