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

Add custom healthcheckextension #400

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
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