Skip to content

Commit 2b2c916

Browse files
matej-gevan-bradley
authored andcommitted
[extension/opamp] Add support for polling interval in HTTP client (open-telemetry#34811)
**Description:** <Describe what has changed.> <!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> Adds support for configuring polling interval for the OpAMP HTTP client. **Link to tracking Issue:** open-telemetry#34749 **Testing:** Adjusted unit test, manually tested **Documentation:** Added to README --------- Signed-off-by: Matej Gera <[email protected]> Co-authored-by: Evan Bradley <[email protected]>
1 parent 85df63a commit 2b2c916

File tree

5 files changed

+121
-23
lines changed

5 files changed

+121
-23
lines changed
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: opampextension
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Adds the ability to configure the polling interval for the HTTP client.
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: [34749]
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/opampextension/README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,23 @@ The following settings are required:
1919
- `ws`: The OpAMP websocket transport settings.
2020
- `endpoint` (no default): The OpAMP server websocket endpoint (URL).
2121

22-
The following settings are optional:
22+
The following settings are optional for the websocket client:
2323

2424
- `server`: The OpAMP server connection settings.
2525
- `ws`: The OpAMP websocket transport settings.
2626
- `tls`: TLS settings.
2727
- `headers`: HTTP headers to set.
28+
29+
The following settings are optional for the HTTP client:
30+
31+
- `server`: The OpAMP server connection settings.
32+
- `http`: The OpAMP websocket transport settings.
33+
- `tls`: TLS settings.
34+
- `headers`: HTTP headers to set.
35+
- `polling_interval`: The interval at which the extension will poll the server. Defaults to 30s.
36+
37+
The following settings are optional for both transports:
38+
2839
- `instance_uid`: A UUIDv7 formatted as a 36 character string in canonical
2940
representation. Auto-generated on start if missing. Setting this ensures the
3041
instance UID remains constant across process restarts.

extension/opampextension/config.go

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ import (
1515
"go.uber.org/zap"
1616
)
1717

18+
// Default value for HTTP client's polling interval, set to 30 seconds in
19+
// accordance with the OpAMP spec.
20+
const httpPollingIntervalDefault = 30 * time.Second
21+
1822
// Config contains the configuration for the opamp extension. Trying to mirror
1923
// the OpAMP supervisor config for some consistency.
2024
type Config struct {
@@ -68,12 +72,6 @@ type commonFields struct {
6872
Headers map[string]configopaque.String `mapstructure:"headers,omitempty"`
6973
}
7074

71-
// OpAMPServer contains the OpAMP transport configuration.
72-
type OpAMPServer struct {
73-
WS *commonFields `mapstructure:"ws,omitempty"`
74-
HTTP *commonFields `mapstructure:"http,omitempty"`
75-
}
76-
7775
func (c *commonFields) Scheme() string {
7876
uri, err := url.ParseRequestURI(c.Endpoint)
7977
if err != nil {
@@ -89,11 +87,38 @@ func (c *commonFields) Validate() error {
8987
return nil
9088
}
9189

90+
type httpFields struct {
91+
commonFields `mapstructure:",squash"`
92+
93+
PollingInterval time.Duration `mapstructure:"polling_interval"`
94+
}
95+
96+
func (h *httpFields) Validate() error {
97+
if err := h.commonFields.Validate(); err != nil {
98+
return err
99+
}
100+
101+
if h.PollingInterval < 0 {
102+
return errors.New("polling interval must be 0 or greater")
103+
}
104+
105+
return nil
106+
}
107+
108+
// OpAMPServer contains the OpAMP transport configuration.
109+
type OpAMPServer struct {
110+
WS *commonFields `mapstructure:"ws,omitempty"`
111+
HTTP *httpFields `mapstructure:"http,omitempty"`
112+
}
113+
92114
func (s OpAMPServer) GetClient(logger *zap.Logger) client.OpAMPClient {
93115
if s.WS != nil {
94116
return client.NewWebSocket(newLoggerFromZap(logger.With(zap.String("client", "ws"))))
95117
}
96-
return client.NewHTTP(newLoggerFromZap(logger.With(zap.String("client", "http"))))
118+
119+
httpClient := client.NewHTTP(newLoggerFromZap(logger.With(zap.String("client", "http"))))
120+
httpClient.SetPollingInterval(s.GetPollingInterval())
121+
return httpClient
97122
}
98123

99124
func (s OpAMPServer) GetHeaders() map[string]configopaque.String {
@@ -123,6 +148,14 @@ func (s OpAMPServer) GetEndpoint() string {
123148
return ""
124149
}
125150

151+
func (s OpAMPServer) GetPollingInterval() time.Duration {
152+
if s.HTTP != nil && s.HTTP.PollingInterval > 0 {
153+
return s.HTTP.PollingInterval
154+
}
155+
156+
return httpPollingIntervalDefault
157+
}
158+
126159
// Validate checks if the extension configuration is valid
127160
func (cfg *Config) Validate() error {
128161
switch {

extension/opampextension/config_test.go

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,11 @@ func TestUnmarshalHttpConfig(t *testing.T) {
5353
assert.Equal(t,
5454
&Config{
5555
Server: &OpAMPServer{
56-
HTTP: &commonFields{
57-
Endpoint: "https://127.0.0.1:4320/v1/opamp",
56+
HTTP: &httpFields{
57+
commonFields: commonFields{
58+
Endpoint: "https://127.0.0.1:4320/v1/opamp",
59+
},
60+
PollingInterval: 1 * time.Minute,
5861
},
5962
},
6063
InstanceUID: "01BX5ZZKBKACTAV9WEVGEMMVRZ",
@@ -115,13 +118,15 @@ func TestConfig_Getters(t *testing.T) {
115118
name: "HTTP valid endpoint and valid instance id",
116119
fields: fields{
117120
Server: &OpAMPServer{
118-
HTTP: &commonFields{
119-
Endpoint: "https://127.0.0.1:4320/v1/opamp",
120-
Headers: map[string]configopaque.String{
121-
"test": configopaque.String("test"),
122-
},
123-
TLSSetting: configtls.ClientConfig{
124-
Insecure: true,
121+
HTTP: &httpFields{
122+
commonFields: commonFields{
123+
Endpoint: "https://127.0.0.1:4320/v1/opamp",
124+
Headers: map[string]configopaque.String{
125+
"test": configopaque.String("test"),
126+
},
127+
TLSSetting: configtls.ClientConfig{
128+
Insecure: true,
129+
},
125130
},
126131
},
127132
},
@@ -194,7 +199,7 @@ func TestConfig_Validate(t *testing.T) {
194199
name: "HTTP must have endpoint",
195200
fields: fields{
196201
Server: &OpAMPServer{
197-
HTTP: &commonFields{},
202+
HTTP: &httpFields{},
198203
},
199204
},
200205
wantErr: func(t assert.TestingT, err error, _ ...any) bool {
@@ -205,8 +210,10 @@ func TestConfig_Validate(t *testing.T) {
205210
name: "HTTP valid endpoint and invalid instance id",
206211
fields: fields{
207212
Server: &OpAMPServer{
208-
HTTP: &commonFields{
209-
Endpoint: "https://127.0.0.1:4320/v1/opamp",
213+
HTTP: &httpFields{
214+
commonFields: commonFields{
215+
Endpoint: "https://127.0.0.1:4320/v1/opamp",
216+
},
210217
},
211218
},
212219
InstanceUID: "01BX5ZZKBKACTAV9WEVGEMMVRZFAIL",
@@ -219,14 +226,33 @@ func TestConfig_Validate(t *testing.T) {
219226
name: "HTTP valid endpoint and valid instance id",
220227
fields: fields{
221228
Server: &OpAMPServer{
222-
HTTP: &commonFields{
223-
Endpoint: "https://127.0.0.1:4320/v1/opamp",
229+
HTTP: &httpFields{
230+
commonFields: commonFields{
231+
Endpoint: "https://127.0.0.1:4320/v1/opamp",
232+
},
224233
},
225234
},
226235
InstanceUID: "01BX5ZZKBKACTAV9WEVGEMMVRZ",
227236
},
228237
wantErr: assert.NoError,
229238
},
239+
{
240+
name: "HTTP invalid polling interval",
241+
fields: fields{
242+
Server: &OpAMPServer{
243+
HTTP: &httpFields{
244+
commonFields: commonFields{
245+
Endpoint: "https://127.0.0.1:4320/v1/opamp",
246+
},
247+
PollingInterval: -1,
248+
},
249+
},
250+
InstanceUID: "01BX5ZZKBKACTAV9WEVGEMMVRZ",
251+
},
252+
wantErr: func(t assert.TestingT, err error, _ ...any) bool {
253+
return assert.Equal(t, "polling interval must be 0 or greater", err.Error())
254+
},
255+
},
230256
{
231257
name: "neither config set",
232258
fields: fields{
@@ -241,7 +267,7 @@ func TestConfig_Validate(t *testing.T) {
241267
fields: fields{
242268
Server: &OpAMPServer{
243269
WS: &commonFields{},
244-
HTTP: &commonFields{},
270+
HTTP: &httpFields{},
245271
},
246272
},
247273
wantErr: func(t assert.TestingT, err error, _ ...any) bool {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
server:
22
http:
33
endpoint: https://127.0.0.1:4320/v1/opamp
4+
polling_interval: 1m
45
instance_uid: 01BX5ZZKBKACTAV9WEVGEMMVRZ

0 commit comments

Comments
 (0)