• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 2230167646

23 Dec 2025 03:10PM UTC coverage: 79.705% (+0.02%) from 79.682%
2230167646

push

gitlab-ci

michalkopczan
feat: Add configuration option RetryDownloadCount

Ticket: ME-589
Changelog: Add configuration option RetryDownloadCount. It allows setting the number of
retry attempts when artifact downloading is interrupted e.g. due to network issues.

Signed-off-by: Michal Kopczan <michal.kopczan@northern.tech>

9 of 11 new or added lines in 2 files covered. (81.82%)

1 existing line in 1 file now uncovered.

7882 of 9889 relevant lines covered (79.7%)

13902.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.14
/src/client_shared/config_parser/config_parser.cpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <client_shared/config_parser.hpp>
16

17
#include <string>
18
#include <vector>
19
#include <algorithm>
20

21
#include <common/expected.hpp>
22
#include <common/json.hpp>
23
#include <common/log.hpp>
24

25
namespace mender {
26
namespace client_shared {
27
namespace config_parser {
28

29
using namespace std;
30

31
namespace expected = mender::common::expected;
32
namespace json = mender::common::json;
33
namespace log = mender::common::log;
34
namespace device_tier = mender::common::device_tier;
35

36
const ConfigParserErrorCategoryClass ConfigParserErrorCategory;
37

38
const char *ConfigParserErrorCategoryClass::name() const noexcept {
×
39
        return "ConfigParserErrorCategory";
×
40
}
41

42
string ConfigParserErrorCategoryClass::message(int code) const {
3✔
43
        switch (code) {
3✔
44
        case NoError:
45
                return "Success";
×
46
        case ValidationError:
47
                return "Validation error";
3✔
48
        default:
49
                return "Unknown";
×
50
        }
51
}
52

53
error::Error MakeError(ConfigParserErrorCode code, const string &msg) {
3✔
54
        return error::Error(error_condition(code, ConfigParserErrorCategory), msg);
6✔
55
}
56

57
ExpectedBool MenderConfigFromFile::LoadFile(const string &path) {
328✔
58
        const json::ExpectedJson e_cfg_json = json::LoadFromFile(path);
656✔
59
        if (!e_cfg_json) {
328✔
60
                auto err = e_cfg_json.error();
302✔
61
                return expected::unexpected(err);
604✔
62
        }
63

64
        bool applied = false;
65

66
        const json::Json cfg_json = e_cfg_json.value();
26✔
67

68
        json::ExpectedJson e_cfg_value = cfg_json.Get("DeviceTypeFile");
26✔
69
        if (e_cfg_value) {
26✔
70
                const json::Json value_json = e_cfg_value.value();
13✔
71
                const json::ExpectedString e_cfg_string = value_json.GetString();
13✔
72
                if (e_cfg_string) {
13✔
73
                        this->device_type_file = e_cfg_string.value();
13✔
74
                        applied = true;
75
                }
76
        }
77

78
        e_cfg_value = cfg_json.Get("ServerCertificate");
52✔
79
        if (e_cfg_value) {
26✔
80
                const json::Json value_json = e_cfg_value.value();
7✔
81
                const json::ExpectedString e_cfg_string = value_json.GetString();
7✔
82
                if (e_cfg_string) {
7✔
83
                        this->server_certificate = e_cfg_string.value();
7✔
84
                        applied = true;
85
                }
86
        }
87

88
        e_cfg_value = cfg_json.Get("UpdateLogPath");
52✔
89
        if (e_cfg_value) {
26✔
90
                const json::Json value_json = e_cfg_value.value();
8✔
91
                const json::ExpectedString e_cfg_string = value_json.GetString();
8✔
92
                if (e_cfg_string) {
8✔
93
                        this->update_log_path = e_cfg_string.value();
8✔
94
                        applied = true;
95
                }
96
        }
97

98
        e_cfg_value = cfg_json.Get("TenantToken");
52✔
99
        if (e_cfg_value) {
26✔
100
                const json::Json value_json = e_cfg_value.value();
7✔
101
                const json::ExpectedString e_cfg_string = value_json.GetString();
7✔
102
                if (e_cfg_string) {
7✔
103
                        this->tenant_token = e_cfg_string.value();
7✔
104
                        applied = true;
105
                }
106
        }
107

108
        e_cfg_value = cfg_json.Get("DeviceTier");
52✔
109
        if (e_cfg_value) {
26✔
110
                const json::Json value_json = e_cfg_value.value();
11✔
111
                const json::ExpectedString e_cfg_string = value_json.GetString();
11✔
112
                if (e_cfg_string) {
11✔
113
                        const string &tier = e_cfg_string.value();
11✔
114
                        if (!device_tier::IsValid(tier)) {
11✔
115
                                auto err = MakeError(
116
                                        ConfigParserErrorCode::ValidationError, "Invalid DeviceTier: " + tier);
2✔
117
                                return expected::unexpected(err);
2✔
118
                        }
119
                        this->device_tier = tier;
10✔
120
                        applied = true;
121
                }
122
        }
123

124
        e_cfg_value = cfg_json.Get("DaemonLogLevel");
50✔
125
        if (e_cfg_value) {
25✔
126
                const json::Json value_json = e_cfg_value.value();
9✔
127
                const json::ExpectedString e_cfg_string = value_json.GetString();
9✔
128
                if (e_cfg_string) {
9✔
129
                        this->daemon_log_level = e_cfg_string.value();
9✔
130
                        applied = true;
131
                }
132
        }
133

134
        /* Boolean values now */
135
        e_cfg_value = cfg_json.Get("SkipVerify");
50✔
136
        if (e_cfg_value) {
25✔
137
                const json::Json value_json = e_cfg_value.value();
9✔
138
                const json::ExpectedBool e_cfg_bool = value_json.GetBool();
9✔
139
                if (e_cfg_bool) {
9✔
140
                        this->skip_verify = e_cfg_bool.value();
9✔
141
                        applied = true;
142
                }
143
        }
144

145
        e_cfg_value = cfg_json.Get("UpdatePollIntervalSeconds");
50✔
146
        if (e_cfg_value) {
25✔
147
                const json::Json value_json = e_cfg_value.value();
7✔
148
                const auto e_cfg_int = value_json.Get<int>();
7✔
149
                if (e_cfg_int) {
7✔
150
                        this->update_poll_interval_seconds = e_cfg_int.value();
7✔
151
                        applied = true;
152
                }
153
        }
154

155
        e_cfg_value = cfg_json.Get("InventoryPollIntervalSeconds");
50✔
156
        if (e_cfg_value) {
25✔
157
                const json::Json value_json = e_cfg_value.value();
7✔
158
                const auto e_cfg_int = value_json.Get<int>();
7✔
159
                if (e_cfg_int) {
7✔
160
                        this->inventory_poll_interval_seconds = e_cfg_int.value();
7✔
161
                        applied = true;
162
                }
163
        }
164

165
        e_cfg_value = cfg_json.Get("RetryPollIntervalSeconds");
50✔
166
        if (e_cfg_value) {
25✔
167
                const json::Json value_json = e_cfg_value.value();
7✔
168
                const auto e_cfg_int = value_json.Get<int>();
7✔
169
                if (e_cfg_int) {
7✔
170
                        this->retry_poll_interval_seconds = e_cfg_int.value();
7✔
171
                        applied = true;
172
                }
173
        }
174

175
        e_cfg_value = cfg_json.Get("RetryPollCount");
50✔
176
        if (e_cfg_value) {
25✔
177
                const json::Json value_json = e_cfg_value.value();
7✔
178
                const auto e_cfg_int = value_json.Get<int>();
7✔
179
                if (e_cfg_int) {
7✔
180
                        this->retry_poll_count = e_cfg_int.value();
7✔
181
                        applied = true;
182
                }
183
        }
184

185
        e_cfg_value = cfg_json.Get("StateScriptTimeoutSeconds");
50✔
186
        if (e_cfg_value) {
25✔
187
                const json::Json value_json = e_cfg_value.value();
7✔
188
                const auto e_cfg_int = value_json.Get<int>();
7✔
189
                if (e_cfg_int) {
7✔
190
                        this->state_script_timeout_seconds = e_cfg_int.value();
7✔
191
                        applied = true;
192
                }
193
        }
194

195
        e_cfg_value = cfg_json.Get("StateScriptRetryTimeoutSeconds");
50✔
196
        if (e_cfg_value) {
25✔
197
                const json::Json value_json = e_cfg_value.value();
7✔
198
                const auto e_cfg_int = value_json.Get<int>();
7✔
199
                if (e_cfg_int) {
7✔
200
                        this->state_script_retry_timeout_seconds = e_cfg_int.value();
7✔
201
                        applied = true;
202
                }
203
        }
204

205
        e_cfg_value = cfg_json.Get("StateScriptRetryIntervalSeconds");
50✔
206
        if (e_cfg_value) {
25✔
207
                const json::Json value_json = e_cfg_value.value();
7✔
208
                const auto e_cfg_int = value_json.Get<int>();
7✔
209
                if (e_cfg_int) {
7✔
210
                        this->state_script_retry_interval_seconds = e_cfg_int.value();
7✔
211
                        applied = true;
212
                }
213
        }
214

215
        e_cfg_value = cfg_json.Get("ModuleTimeoutSeconds");
50✔
216
        if (e_cfg_value) {
25✔
217
                const json::Json value_json = e_cfg_value.value();
7✔
218
                const auto e_cfg_int = value_json.Get<int>();
7✔
219
                if (e_cfg_int) {
7✔
220
                        this->module_timeout_seconds = e_cfg_int.value();
7✔
221
                        applied = true;
222
                }
223
        }
224

225

226
        e_cfg_value = cfg_json.Get("ArtifactVerifyKeys");
50✔
227
        if (e_cfg_value) {
25✔
228
                this->artifact_verify_keys.clear();
9✔
229
                const json::Json value_array = e_cfg_value.value();
9✔
230
                const json::ExpectedSize e_n_items = value_array.GetArraySize();
9✔
231
                if (e_n_items) {
9✔
232
                        for (size_t i = 0; i < e_n_items.value(); i++) {
33✔
233
                                const json::ExpectedJson e_array_item = value_array.Get(i);
24✔
234
                                if (e_array_item) {
24✔
235
                                        const json::ExpectedString e_item_string = e_array_item.value().GetString();
24✔
236
                                        if (e_item_string) {
24✔
237
                                                const string item_value = e_item_string.value();
24✔
238
                                                if (count(
24✔
239
                                                                this->artifact_verify_keys.begin(),
240
                                                                this->artifact_verify_keys.end(),
241
                                                                item_value)
242
                                                        == 0) {
243
                                                        this->artifact_verify_keys.push_back(item_value);
24✔
244
                                                        applied = true;
245
                                                }
246
                                        }
247
                                }
248
                        }
249
                }
250
                artifact_verify_keys_field_used_ = true;
9✔
251
        }
252

253
        e_cfg_value = cfg_json.Get("ArtifactVerifyKey");
50✔
254
        if (e_cfg_value) {
25✔
255
                const json::Json value_json = e_cfg_value.value();
3✔
256
                const json::ExpectedString e_cfg_string = value_json.GetString();
3✔
257
                if (e_cfg_string) {
3✔
258
                        if (artifact_verify_keys_field_used_) {
3✔
259
                                auto err = MakeError(
260
                                        ConfigParserErrorCode::ValidationError,
261
                                        "Both 'ArtifactVerifyKey' and 'ArtifactVerifyKeys' are set");
2✔
262
                                return expected::unexpected(err);
2✔
263
                        }
264
                        assert(artifact_verify_keys.size() <= 1);
265
                        this->artifact_verify_keys.clear();
2✔
266
                        this->artifact_verify_keys.push_back(e_cfg_string.value());
2✔
267
                        applied = true;
268
                }
269
        }
270

271
        e_cfg_value = cfg_json.Get("Servers");
48✔
272
        if (e_cfg_value) {
24✔
273
                this->servers.clear();
9✔
274
                const json::Json value_array = e_cfg_value.value();
9✔
275
                const json::ExpectedSize e_n_items = value_array.GetArraySize();
9✔
276
                if (e_n_items) {
9✔
277
                        for (size_t i = 0; i < e_n_items.value(); i++) {
25✔
278
                                const json::ExpectedJson e_array_item = value_array.Get(i);
16✔
279
                                if (e_array_item) {
16✔
280
                                        const json::ExpectedJson e_item_json = e_array_item.value().Get("ServerURL");
16✔
281
                                        if (e_item_json) {
16✔
282
                                                const json::ExpectedString e_item_string = e_item_json.value().GetString();
16✔
283
                                                if (e_item_string) {
16✔
284
                                                        const string item_value = e_item_string.value();
16✔
285
                                                        if (count(this->servers.begin(), this->servers.end(), item_value)
16✔
286
                                                                == 0) {
287
                                                                this->servers.push_back(std::move(item_value));
16✔
288
                                                                applied = true;
289
                                                        }
290
                                                }
291
                                        }
292
                                }
293
                        }
294
                }
295
                servers_field_used_ = true;
9✔
296
        }
297

298
        e_cfg_value = cfg_json.Get("ServerURL");
48✔
299
        if (e_cfg_value) {
24✔
300
                const json::Json value_json = e_cfg_value.value();
6✔
301
                const json::ExpectedString e_cfg_string = value_json.GetString();
6✔
302
                if (e_cfg_string) {
6✔
303
                        if (servers_field_used_) {
6✔
304
                                auto err = MakeError(
305
                                        ConfigParserErrorCode::ValidationError,
306
                                        "Both 'Servers' AND 'ServerURL given in the configuration. Please set only one of these fields");
2✔
307
                                return expected::unexpected(err);
2✔
308
                        }
309
                        assert(servers.size() <= 1);
310
                        this->servers.clear();
5✔
311
                        this->servers.push_back(e_cfg_string.value());
5✔
312
                        applied = true;
313
                }
314
        }
315

316
        /* Last but not least, complex values */
317
        e_cfg_value = cfg_json.Get("HttpsClient");
46✔
318
        if (e_cfg_value) {
23✔
319
                const json::Json value_json = e_cfg_value.value();
8✔
320
                json::ExpectedJson e_cfg_subval = value_json.Get("Certificate");
8✔
321
                if (e_cfg_subval) {
8✔
322
                        const json::Json subval_json = e_cfg_subval.value();
8✔
323
                        const json::ExpectedString e_cfg_string = subval_json.GetString();
8✔
324
                        if (e_cfg_string) {
8✔
325
                                this->https_client.certificate = e_cfg_string.value();
8✔
326
                                applied = true;
327
                        }
328
                }
329

330
                e_cfg_subval = value_json.Get("Key");
16✔
331
                if (e_cfg_subval) {
8✔
332
                        const json::Json subval_json = e_cfg_subval.value();
7✔
333
                        const json::ExpectedString e_cfg_string = subval_json.GetString();
7✔
334
                        if (e_cfg_string) {
7✔
335
                                this->https_client.key = e_cfg_string.value();
7✔
336
                                applied = true;
337
                        }
338
                }
339

340
                e_cfg_subval = value_json.Get("SSLEngine");
16✔
341
                if (e_cfg_subval) {
8✔
342
                        const json::Json subval_json = e_cfg_subval.value();
7✔
343
                        const json::ExpectedString e_cfg_string = subval_json.GetString();
7✔
344
                        if (e_cfg_string) {
7✔
345
                                this->https_client.ssl_engine = e_cfg_string.value();
7✔
346
                                applied = true;
347
                        }
348
                }
349
        }
350

351
        e_cfg_value = cfg_json.Get("Security");
46✔
352
        if (e_cfg_value) {
23✔
353
                const json::Json value_json = e_cfg_value.value();
7✔
354
                json::ExpectedJson e_cfg_subval = value_json.Get("AuthPrivateKey");
7✔
355
                if (e_cfg_subval) {
7✔
356
                        const json::Json subval_json = e_cfg_subval.value();
7✔
357
                        const json::ExpectedString e_cfg_string = subval_json.GetString();
7✔
358
                        if (e_cfg_string) {
7✔
359
                                this->security.auth_private_key = e_cfg_string.value();
7✔
360
                                applied = true;
361
                        }
362
                }
363

364
                e_cfg_subval = value_json.Get("SSLEngine");
14✔
365
                if (e_cfg_subval) {
7✔
366
                        const json::Json subval_json = e_cfg_subval.value();
7✔
367
                        const json::ExpectedString e_cfg_string = subval_json.GetString();
7✔
368
                        if (e_cfg_string) {
7✔
369
                                this->security.ssl_engine = e_cfg_string.value();
7✔
370
                                applied = true;
371
                        }
372
                }
373
        }
374

375
        e_cfg_value = cfg_json.Get("RetryDownloadCount");
46✔
376
        if (e_cfg_value) {
23✔
377
                const json::Json value_json = e_cfg_value.value();
9✔
378
                const auto e_cfg_int = value_json.Get<int>();
9✔
379
                if (e_cfg_int) {
9✔
380
                        if (e_cfg_int.value() < retry_download_count_min
9✔
381
                                || e_cfg_int.value() > retry_download_count_max) {
9✔
382
                                auto err = MakeError(
383
                                        ConfigParserErrorCode::ValidationError,
NEW
384
                                        "RetryDownloadCount outside of allowed values.");
×
NEW
385
                                return expected::unexpected(err);
×
386
                        }
387
                        this->retry_download_count = e_cfg_int.value();
9✔
388
                        applied = true;
389
                }
390
        }
391

392
        return applied;
393
}
394

395
void MenderConfigFromFile::Reset() {
1✔
396
        *this = MenderConfigFromFile();
1✔
397
}
1✔
398

399
} // namespace config_parser
400
} // namespace client_shared
401
} // namespace mender
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc