Skip to content

Commit d33d815

Browse files
committed
Migrate healthcheckextension to internal/healthcheck implementation
Signed-off-by: Israel Blancas <[email protected]>
1 parent feddc1a commit d33d815

File tree

15 files changed

+516
-636
lines changed

15 files changed

+516
-636
lines changed

.chloggen/42256.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: healthcheckextension
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: "Migrated healthcheckextension implementation to use the internal/healtcheck. Added extension.healthcheck.useCompatibilityWrapper feature gate to enable new behavior."
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [42256]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

extension/healthcheckextension/README.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Health Check
22

3+
> ℹ️ **Migration Notice** ℹ️
4+
>
5+
> This extension has been migrated to use the shared healthcheck implementation from `internal/healthcheck`
6+
> while maintaining full backward compatibility. See the [Backward Compatibility](#backward-compatibility)
7+
> section for details about feature gates and migration options.
8+
39
> ⚠️⚠️⚠️ **Warning** ⚠️⚠️⚠️
410
>
511
> The `check_collector_pipeline` feature of this extension is not working as expected. It
@@ -53,5 +59,45 @@ extensions:
5359
unhealthy: I'm bad!
5460
```
5561
56-
The full list of settings exposed for this exporter is documented in [config.go](./config.go)
62+
The full list of settings exposed for this exporter is documented in [LegacyConfig in config.go](../../internal/healthcheck/internal/http/config.go#L24)
5763
with detailed sample configurations in [testdata/config.yaml](./testdata/config.yaml).
64+
65+
## Backward Compatibility
66+
67+
[Linked issue](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/42256).
68+
69+
This extension maintains full backward compatibility with the original Ready/NotReady behavior through a compatibility wrapper controlled by a feature gate.
70+
71+
### Feature Gate: `extension.healthcheck.useCompatibilityWrapper`
72+
73+
- **Default**: Enabled (true)
74+
- **Purpose**: Preserves v1 Ready/NotReady behavior when using the shared healthcheck implementation
75+
- **When enabled**: Ready/NotReady calls directly control health endpoint status (original behavior)
76+
- **When disabled**: Health status is determined by component status events (v2 behavior)
77+
78+
#### Usage
79+
80+
To disable the compatibility wrapper and use the new event-driven behavior:
81+
82+
```bash
83+
# Set the feature gate to false
84+
--feature-gates=extension.healthcheck.useCompatibilityWrapper=false
85+
```
86+
87+
#### Migration Timeline
88+
89+
1. **Current**: Compatibility wrapper enabled by default - no breaking changes.
90+
2. **Future**: Feature gate will be removed, compatibility wrapper will be permanently disabled.
91+
3. **Recommended**: Test your setup with the feature gate disabled to prepare for future versions.
92+
93+
#### Ready/NotReady Behavior
94+
95+
**With Compatibility Wrapper (Default)**
96+
- `Ready()` → Health endpoint returns 200 OK
97+
- `NotReady()` → Health endpoint returns 503 Service Unavailable
98+
- Behavior identical to original extension
99+
100+
**Without Compatibility Wrapper**
101+
- `Ready()`/`NotReady()` → Used for pipeline lifecycle only
102+
- Health status determined by component status events
103+
- Behavior similar to healthcheckv2extension

extension/healthcheckextension/config.go

Lines changed: 0 additions & 74 deletions
This file was deleted.

extension/healthcheckextension/config_test.go

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"go.opentelemetry.io/collector/confmap/xconfmap"
1818

1919
"github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension/internal/metadata"
20+
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/healthcheck"
2021
)
2122

2223
func TestLoadConfig(t *testing.T) {
@@ -33,39 +34,58 @@ func TestLoadConfig(t *testing.T) {
3334
},
3435
{
3536
id: component.NewIDWithName(metadata.Type, "1"),
36-
expected: &Config{
37-
ServerConfig: confighttp.ServerConfig{
38-
Endpoint: "localhost:13",
39-
TLS: configoptional.Some(configtls.ServerConfig{
40-
Config: configtls.Config{
41-
CAFile: "/path/to/ca",
42-
CertFile: "/path/to/cert",
43-
KeyFile: "/path/to/key",
44-
},
45-
}),
37+
expected: &healthcheck.Config{
38+
LegacyConfig: healthcheck.HTTPLegacyConfig{
39+
ServerConfig: confighttp.ServerConfig{
40+
Endpoint: "localhost:13",
41+
TLS: configoptional.Some(configtls.ServerConfig{
42+
Config: configtls.Config{
43+
CAFile: "/path/to/ca",
44+
CertFile: "/path/to/cert",
45+
KeyFile: "/path/to/key",
46+
},
47+
}),
48+
},
49+
Path: "/",
50+
CheckCollectorPipeline: &healthcheck.CheckCollectorPipelineConfig{
51+
Enabled: false,
52+
Interval: "5m",
53+
ExporterFailureThreshold: 5,
54+
},
55+
UseV2: false,
4656
},
47-
CheckCollectorPipeline: defaultCheckCollectorPipelineSettings(),
48-
Path: "/",
49-
ResponseBody: nil,
5057
},
5158
},
5259
{
5360
id: component.NewIDWithName(metadata.Type, "missingendpoint"),
54-
expectedErr: errNoEndpointProvided,
61+
expectedErr: healthcheck.ErrHTTPEndpointRequired,
5562
},
5663
{
57-
id: component.NewIDWithName(metadata.Type, "invalidthreshold"),
58-
expectedErr: errInvalidExporterFailureThresholdProvided,
64+
id: component.NewIDWithName(metadata.Type, "invalidthreshold"),
65+
expected: &healthcheck.Config{
66+
LegacyConfig: healthcheck.HTTPLegacyConfig{
67+
ServerConfig: confighttp.ServerConfig{
68+
Endpoint: "localhost:13",
69+
},
70+
Path: "/",
71+
CheckCollectorPipeline: &healthcheck.CheckCollectorPipelineConfig{
72+
Enabled: false,
73+
Interval: "5m",
74+
ExporterFailureThreshold: -1,
75+
},
76+
UseV2: false,
77+
},
78+
},
5979
},
6080
{
6181
id: component.NewIDWithName(metadata.Type, "invalidpath"),
62-
expectedErr: errInvalidPath,
82+
expectedErr: healthcheck.ErrInvalidPath,
6383
},
6484
{
6585
id: component.NewIDWithName(metadata.Type, "response-body"),
6686
expected: func() component.Config {
67-
cfg := NewFactory().CreateDefaultConfig().(*Config)
68-
cfg.ResponseBody = &ResponseBodySettings{
87+
cfg := NewFactory().CreateDefaultConfig().(*healthcheck.Config)
88+
cfg.ResponseBody = &healthcheck.ResponseBodyConfig{
6989
Healthy: "I'm OK",
7090
Unhealthy: "I'm not well",
7191
}

extension/healthcheckextension/factory.go

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,25 @@ import (
99
"go.opentelemetry.io/collector/component"
1010
"go.opentelemetry.io/collector/config/confighttp"
1111
"go.opentelemetry.io/collector/extension"
12+
"go.opentelemetry.io/collector/featuregate"
1213

1314
"github.com/open-telemetry/opentelemetry-collector-contrib/extension/healthcheckextension/internal/metadata"
1415
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/testutil"
16+
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/healthcheck"
1517
)
1618

1719
const defaultPort = 13133
1820

21+
// Feature gate to enable the compatibility wrapper that preserves v1 Ready/NotReady behavior
22+
var useCompatibilityWrapperGate = featuregate.GlobalRegistry().MustRegister(
23+
"extension.healthcheck.useCompatibilityWrapper",
24+
featuregate.StageBeta,
25+
featuregate.WithRegisterDescription("Use compatibility wrapper to preserve v1 Ready/NotReady behavior when using shared healthcheck implementation"),
26+
featuregate.WithRegisterFromVersion("v0.135.0"),
27+
featuregate.WithRegisterToVersion("v0.140.0"),
28+
featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/42256"),
29+
)
30+
1931
// NewFactory creates a factory for HealthCheck extension.
2032
func NewFactory() extension.Factory {
2133
return extension.NewFactory(
@@ -27,26 +39,34 @@ func NewFactory() extension.Factory {
2739
}
2840

2941
func createDefaultConfig() component.Config {
30-
return &Config{
31-
ServerConfig: confighttp.ServerConfig{
32-
Endpoint: testutil.EndpointForPort(defaultPort),
42+
return &healthcheck.Config{
43+
LegacyConfig: healthcheck.HTTPLegacyConfig{
44+
ServerConfig: confighttp.ServerConfig{
45+
Endpoint: testutil.EndpointForPort(defaultPort),
46+
},
47+
Path: "/",
48+
CheckCollectorPipeline: &healthcheck.CheckCollectorPipelineConfig{
49+
Enabled: false,
50+
Interval: "5m",
51+
ExporterFailureThreshold: 5,
52+
},
53+
UseV2: false,
3354
},
34-
CheckCollectorPipeline: defaultCheckCollectorPipelineSettings(),
35-
Path: "/",
3655
}
3756
}
3857

39-
func createExtension(_ context.Context, set extension.Settings, cfg component.Config) (extension.Extension, error) {
40-
config := cfg.(*Config)
58+
func createExtension(ctx context.Context, set extension.Settings, cfg component.Config) (extension.Extension, error) {
59+
config := cfg.(*healthcheck.Config)
4160

42-
return newServer(*config, set.TelemetrySettings), nil
43-
}
61+
// Create the shared health check extension
62+
sharedExt := healthcheck.NewHealthCheckExtension(ctx, *config, set)
4463

45-
// defaultCheckCollectorPipelineSettings returns the default settings for CheckCollectorPipeline.
46-
func defaultCheckCollectorPipelineSettings() checkCollectorPipelineSettings {
47-
return checkCollectorPipelineSettings{
48-
Enabled: false,
49-
Interval: "5m",
50-
ExporterFailureThreshold: 5,
64+
// Conditionally wrap with compatibility layer based on feature gate
65+
if useCompatibilityWrapperGate.IsEnabled() {
66+
// Use compatibility wrapper to preserve v1 Ready/NotReady behavior
67+
return newCompatibilityWrapper(sharedExt), nil
5168
}
69+
70+
// Use shared implementation directly (new behavior)
71+
return sharedExt, nil
5272
}

0 commit comments

Comments
 (0)