Skip to content

Commit f7a009a

Browse files
Julio-GuerraHellzy
andauthored
internal/remoteconfig: client error handling (backport of release-v1.45.x fixes) (#1627)
Properly handle many error cases that were not in the remote config client. Especially the "no remote config update" case {} that is leading to a json parsing error in the repository Update(). Backport of release-v1.45.x changes Co-authored-by: François Mazeau <[email protected]>
1 parent 2d25957 commit f7a009a

File tree

2 files changed

+66
-35
lines changed

2 files changed

+66
-35
lines changed

internal/appsec/remoteconfig.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,7 @@ func (a *appsec) asmFeaturesCallback(u remoteconfig.ProductUpdate) map[string]rc
7474
a.stop()
7575
}
7676
if err != nil {
77-
status.State = rc.ApplyStateError
78-
status.Error = err.Error()
77+
status = genApplyStatus(false, err)
7978
}
8079
statuses[path] = status
8180
}
@@ -97,6 +96,15 @@ func (h *wafHandleWrapper) asmDataCallback(u remoteconfig.ProductUpdate) map[str
9796

9897
for path, raw := range u {
9998
log.Debug("appsec: Remote config: processing %s", path)
99+
100+
// A nil config means ASM_DATA was disabled, and we stopped receiving the config file
101+
// Don't ack the config in this case
102+
if raw == nil {
103+
log.Debug("appsec: remote config: %s disabled", path)
104+
statuses[path] = genApplyStatus(false, nil)
105+
continue
106+
}
107+
100108
var rulesData rc.ASMDataRulesData
101109
if err := json.Unmarshal(raw, &rulesData); err != nil {
102110
log.Debug("appsec: Remote config: error while unmarshalling payload for %s: %v. Configuration won't be applied.", path, err)

internal/remoteconfig/remoteconfig.go

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
rc "github.com/DataDog/datadog-agent/pkg/remoteconfig/state"
2222

2323
"gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig"
24+
"gopkg.in/DataDog/dd-trace-go.v1/internal/log"
2425
"gopkg.in/DataDog/dd-trace-go.v1/internal/version"
2526
)
2627

@@ -148,19 +149,19 @@ func (c *Client) Stop() {
148149
func (c *Client) updateState() {
149150
data, err := c.newUpdateRequest()
150151
if err != nil {
151-
c.lastError = err
152+
log.Error("remoteconfig: unexpected error while creating a new update request payload: %v", err)
152153
return
153154
}
154155

155156
req, err := http.NewRequest(http.MethodGet, c.endpoint, &data)
156157
if err != nil {
157-
c.lastError = err
158+
log.Error("remoteconfig: unexpected error while creating a new http request: %v", err)
158159
return
159160
}
160161

161162
resp, err := c.HTTP.Do(req)
162163
if err != nil {
163-
c.lastError = err
164+
log.Debug("remoteconfig: http request error: %v", err)
164165
return
165166
}
166167
// Flush and close the response body when returning (cf. https://pkg.go.dev/net/http#Client.Do)
@@ -169,15 +170,28 @@ func (c *Client) updateState() {
169170
resp.Body.Close()
170171
}()
171172

172-
var update clientGetConfigsResponse
173-
err = json.NewDecoder(resp.Body).Decode(&update)
173+
if sc := resp.StatusCode; sc != http.StatusOK {
174+
log.Debug("remoteconfig: http request error: response status code is not 200 (OK) but %s", http.StatusText(sc))
175+
return
176+
}
177+
178+
respBody, err := io.ReadAll(resp.Body)
174179
if err != nil {
175-
c.lastError = err
180+
log.Error("remoteconfig: http request error: could not read the response body: %v", err)
176181
return
177182
}
178183

179-
err = c.applyUpdate(&update)
180-
c.lastError = err
184+
if body := string(respBody); body == `{}` || body == `null` {
185+
return
186+
}
187+
188+
var update clientGetConfigsResponse
189+
if err := json.Unmarshal(respBody, &update); err != nil {
190+
log.Error("remoteconfig: http request error: could not parse the json response body: %v", err)
191+
return
192+
}
193+
194+
c.lastError = c.applyUpdate(&update)
181195
}
182196

183197
// RegisterCallback allows registering a callback that will be invoked when the client
@@ -199,13 +213,6 @@ func (c *Client) applyUpdate(pbUpdate *clientGetConfigsResponse) error {
199213
}
200214
}
201215

202-
update := rc.Update{
203-
TUFRoots: pbUpdate.Roots,
204-
TUFTargets: pbUpdate.Targets,
205-
TargetFiles: fileMap,
206-
ClientConfigs: pbUpdate.ClientConfigs,
207-
}
208-
209216
mapify := func(s *rc.RepositoryState) map[string]string {
210217
m := make(map[string]string)
211218
for i := range s.Configs {
@@ -219,30 +226,43 @@ func (c *Client) applyUpdate(pbUpdate *clientGetConfigsResponse) error {
219226
// Check the repository state before and after the update to detect which configs are not being sent anymore.
220227
// This is needed because some products can stop sending configurations, and we want to make sure that the subscribers
221228
// are provided with this information in this case
222-
stateBefore, _ := c.repository.CurrentState()
223-
products, err := c.repository.Update(update)
224-
stateAfter, _ := c.repository.CurrentState()
229+
stateBefore, err := c.repository.CurrentState()
230+
if err != nil {
231+
return fmt.Errorf("repository current state error: %v", err)
232+
}
233+
products, err := c.repository.Update(rc.Update{
234+
TUFRoots: pbUpdate.Roots,
235+
TUFTargets: pbUpdate.Targets,
236+
TargetFiles: fileMap,
237+
ClientConfigs: pbUpdate.ClientConfigs,
238+
})
239+
if err != nil {
240+
return fmt.Errorf("repository update error: %v", err)
241+
}
242+
stateAfter, err := c.repository.CurrentState()
243+
if err != nil {
244+
return fmt.Errorf("repository current state error after update: %v", err)
245+
}
225246

226247
// Create a config files diff between before/after the update to see which config files are missing
227248
mBefore := mapify(&stateBefore)
228-
mAfter := mapify(&stateAfter)
229-
for k := range mAfter {
249+
for k := range mapify(&stateAfter) {
230250
delete(mBefore, k)
231251
}
232252

233253
// Set the payload data to nil for missing config files. The callbacks then can handle the nil config case to detect
234254
// that this config will not be updated anymore.
235-
updatedProducts := make(map[string]bool)
255+
updatedProducts := make(map[string]struct{})
236256
for path, product := range mBefore {
237257
if productUpdates[product] == nil {
238258
productUpdates[product] = make(ProductUpdate)
239259
}
240260
productUpdates[product][path] = nil
241-
updatedProducts[product] = true
261+
updatedProducts[product] = struct{}{}
242262
}
243263
// Aggregate updated products and missing products so that callbacks get called for both
244264
for _, p := range products {
245-
updatedProducts[p] = true
265+
updatedProducts[p] = struct{}{}
246266
}
247267

248268
// Performs the callbacks registered for all updated products and update the application status in the repository
@@ -255,7 +275,7 @@ func (c *Client) applyUpdate(pbUpdate *clientGetConfigsResponse) error {
255275
}
256276
}
257277

258-
return err
278+
return nil
259279
}
260280

261281
func (c *Client) newUpdateRequest() (bytes.Buffer, error) {
@@ -290,15 +310,18 @@ func (c *Client) newUpdateRequest() (bytes.Buffer, error) {
290310
errMsg = c.lastError.Error()
291311
}
292312

293-
pbConfigState := make([]*configState, 0, len(state.Configs))
294-
for _, f := range state.Configs {
295-
pbConfigState = append(pbConfigState, &configState{
296-
ID: f.ID,
297-
Version: f.Version,
298-
Product: f.Product,
299-
ApplyState: f.ApplyStatus.State,
300-
ApplyError: f.ApplyStatus.Error,
301-
})
313+
var pbConfigState []*configState
314+
if !hasError {
315+
pbConfigState = make([]*configState, 0, len(state.Configs))
316+
for _, f := range state.Configs {
317+
pbConfigState = append(pbConfigState, &configState{
318+
ID: f.ID,
319+
Version: f.Version,
320+
Product: f.Product,
321+
ApplyState: f.ApplyStatus.State,
322+
ApplyError: f.ApplyStatus.Error,
323+
})
324+
}
302325
}
303326

304327
cap := big.NewInt(0)

0 commit comments

Comments
 (0)