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

mendersoftware / mender / 1019717806

28 Sep 2023 01:24PM UTC coverage: 78.05% (-0.1%) from 78.153%
1019717806

push

gitlab-ci

kacf
test: Drain event queue before checking number of connections.

One http instance was moved because Boost accesses its members even
after they are destroyed. It shouldn't, but this is unlikely to happen
in production where the loop is continuously running.

Signed-off-by: Kristian Amlie <kristian.amlie@northern.tech>

6749 of 8647 relevant lines covered (78.05%)

10652.94 hits per line

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

88.33
/mender-update/daemon/context.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 <mender-update/daemon/context.hpp>
16

17
#include <common/common.hpp>
18
#include <common/conf.hpp>
19
#include <common/log.hpp>
20

21
namespace mender {
22
namespace update {
23
namespace daemon {
24

25
namespace common = mender::common;
26
namespace conf = mender::common::conf;
27
namespace log = mender::common::log;
28

29
namespace main_context = mender::update::context;
30

31
const int kStateDataVersion = 2;
32

33
// The maximum times we are allowed to move through update states. If this is exceeded then the
34
// update will be forcefully aborted. This can happen if we are in a reboot loop, for example.
35
const int kMaxStateDataStoreCount = 28;
36

37
ExpectedStateData ApiResponseJsonToStateData(const json::Json &json) {
55✔
38
        StateData data;
110✔
39

40
        expected::ExpectedString str = json.Get("id").and_then(json::ToString);
110✔
41
        if (!str) {
55✔
42
                return expected::unexpected(str.error().WithContext("Could not get deployment ID"));
×
43
        }
44
        data.update_info.id = str.value();
55✔
45

46
        str = json.Get("artifact")
55✔
47
                          .and_then([](const json::Json &json) { return json.Get("source"); })
165✔
48
                          .and_then([](const json::Json &json) { return json.Get("uri"); })
165✔
49
                          .and_then(json::ToString);
55✔
50
        if (!str) {
55✔
51
                return expected::unexpected(
×
52
                        str.error().WithContext("Could not get artifact URI for deployment"));
×
53
        }
54
        data.update_info.artifact.source.uri = str.value();
55✔
55
        log::Debug("Artifact Download URL: " + data.update_info.artifact.source.uri);
55✔
56

57
        str = json.Get("artifact")
55✔
58
                          .and_then([](const json::Json &json) { return json.Get("source"); })
165✔
59
                          .and_then([](const json::Json &json) { return json.Get("expire"); })
165✔
60
                          .and_then(json::ToString);
55✔
61
        if (str) {
55✔
62
                data.update_info.artifact.source.expire = str.value();
55✔
63
                // If it's not available, we don't care.
64
        }
65

66
        // For later: Update Control Maps should be handled here.
67

68
        // Note: There is more information available in the response than we collect here, but we
69
        // prefer to get the information from the artifact instead, since it is the authoritative
70
        // source. And it's also signed, unlike the response.
71

72
        return data;
55✔
73
}
74

75
// Database keys
76
const string Context::kRollbackNotSupported = "rollback-not-supported";
77
const string Context::kRollbackSupported = "rollback-supported";
78

79
string SupportsRollbackToDbString(bool support) {
43✔
80
        return support ? Context::kRollbackSupported : Context::kRollbackNotSupported;
43✔
81
}
82

83
expected::ExpectedBool DbStringToSupportsRollback(const string &str) {
14✔
84
        if (str == Context::kRollbackSupported) {
14✔
85
                return true;
12✔
86
        } else if (str == Context::kRollbackNotSupported) {
2✔
87
                return false;
2✔
88
        } else {
89
                return expected::unexpected(main_context::MakeError(
×
90
                        main_context::DatabaseValueError,
91
                        "\"" + str + "\" is not a valid value for SupportsRollback"));
×
92
        }
93
}
94

95
// Database keys
96
const string Context::kRebootTypeNone = "";
97
const string Context::kRebootTypeCustom = "reboot-type-custom";
98
const string Context::kRebootTypeAutomatic = "reboot-type-automatic";
99

100
string NeedsRebootToDbString(update_module::RebootAction action) {
70✔
101
        switch (action) {
70✔
102
        case update_module::RebootAction::No:
8✔
103
                return Context::kRebootTypeNone;
8✔
104
        case update_module::RebootAction::Automatic:
×
105
                return Context::kRebootTypeAutomatic;
×
106
        case update_module::RebootAction::Yes:
62✔
107
                return Context::kRebootTypeCustom;
62✔
108
        default:
×
109
                // Should not happen.
110
                assert(false);
×
111
                return Context::kRebootTypeNone;
112
        }
113
}
114

115
update_module::ExpectedRebootAction DbStringToNeedsReboot(const string &str) {
48✔
116
        if (str == Context::kRebootTypeNone) {
48✔
117
                return update_module::RebootAction::No;
×
118
        } else if (str == Context::kRebootTypeAutomatic) {
48✔
119
                return update_module::RebootAction::Automatic;
×
120
        } else if (str == Context::kRebootTypeCustom) {
48✔
121
                return update_module::RebootAction::Yes;
48✔
122
        } else {
123
                return expected::unexpected(main_context::MakeError(
×
124
                        main_context::DatabaseValueError,
125
                        "\"" + str + "\" is not a valid value for RebootRequested"));
×
126
        }
127
}
128

129
void StateData::FillUpdateDataFromArtifact(artifact::PayloadHeaderView &view) {
49✔
130
        auto &artifact = update_info.artifact;
49✔
131
        auto &header = view.header;
49✔
132
        artifact.compatible_devices = header.header_info.depends.device_type;
49✔
133
        artifact.payload_types = {header.payload_type};
98✔
134
        artifact.artifact_name = header.artifact_name;
49✔
135
        artifact.artifact_group = header.artifact_group;
49✔
136
        if (header.type_info.artifact_provides) {
49✔
137
                artifact.type_info_provides = header.type_info.artifact_provides.value();
48✔
138
        } else {
139
                artifact.type_info_provides.clear();
1✔
140
        }
141
        if (header.type_info.clears_artifact_provides) {
49✔
142
                artifact.clears_artifact_provides = header.type_info.clears_artifact_provides.value();
48✔
143
        } else {
144
                artifact.clears_artifact_provides.clear();
1✔
145
        }
146
}
49✔
147

148
Context::Context(main_context::MenderContext &mender_context, events::EventLoop &event_loop) :
90✔
149
        mender_context(mender_context),
150
        event_loop(event_loop),
151
        authenticator(
152
                event_loop,
153
                http::ClientConfig(mender_context.GetConfig().server_certificate),
180✔
154
                mender_context.GetConfig().server_url,
90✔
155
                mender_context.GetConfig().paths.GetKeyFile(),
180✔
156
                mender_context.GetConfig().paths.GetIdentityScript()),
180✔
157
        http_client(
158
                http::ClientConfig(mender_context.GetConfig().server_certificate),
180✔
159
                event_loop,
160
                authenticator),
90✔
161
        download_client(http::ClientConfig(mender_context.GetConfig().server_certificate), event_loop),
180✔
162
        deployment_client(make_shared<deployments::DeploymentClient>()),
90✔
163
        inventory_client(make_shared<inventory::InventoryClient>()) {
720✔
164
}
90✔
165

166
///////////////////////////////////////////////////////////////////////////////////////////////////
167
// Values for various states in the database.
168
///////////////////////////////////////////////////////////////////////////////////////////////////
169

170
// In use by current client. Some of the variable names have been updated from the Golang version,
171
// but the database strings are the same. Some naming is inconsistent, this is for historical
172
// reasons, and it's better to look at the names for the variables.
173
const string Context::kUpdateStateDownload = "update-store";
174
const string Context::kUpdateStateArtifactInstall = "update-install";
175
const string Context::kUpdateStateArtifactReboot = "reboot";
176
const string Context::kUpdateStateArtifactVerifyReboot = "after-reboot";
177
const string Context::kUpdateStateArtifactCommit = "update-commit";
178
const string Context::kUpdateStateAfterArtifactCommit = "update-after-commit";
179
const string Context::kUpdateStateArtifactRollback = "rollback";
180
const string Context::kUpdateStateArtifactRollbackReboot = "rollback-reboot";
181
const string Context::kUpdateStateArtifactVerifyRollbackReboot = "after-rollback-reboot";
182
const string Context::kUpdateStateArtifactFailure = "update-error";
183
const string Context::kUpdateStateCleanup = "cleanup";
184
const string Context::kUpdateStateStatusReportRetry = "update-retry-report";
185

186
///////////////////////////////////////////////////////////////////////////////////////////////////
187
// Not in use by current client, but were in use by Golang client, and still important to handle
188
// correctly in recovery scenarios.
189
///////////////////////////////////////////////////////////////////////////////////////////////////
190

191
// This client doesn't use it, but it's essentially equivalent to "update-after-commit".
192
const string Context::kUpdateStateUpdateAfterFirstCommit = "update-after-first-commit";
193
// This client doesn't use it, but it's essentially equivalent to "after-rollback-reboot".
194
const string Context::kUpdateStateVerifyRollbackReboot = "verify-rollback-reboot";
195
// No longer used. Since this used to be at the very end of an update, if we encounter it in the
196
// database during startup, we just go back to Idle.
197
const string UpdateStateReportStatusError = "status-report-error";
198

199
///////////////////////////////////////////////////////////////////////////////////////////////////
200
// Not in use. All of these, as well as unknown values, will cause a rollback.
201
///////////////////////////////////////////////////////////////////////////////////////////////////
202

203
// Disable, but distinguish from comments.
204
#if false
205
// These were never actually saved due to not being update states.
206
const string Context::kUpdateStateInit = "init";
207
const string Context::kUpdateStateIdle = "idle";
208
const string Context::kUpdateStateAuthorize = "authorize";
209
const string Context::kUpdateStateAuthorizeWait = "authorize-wait";
210
const string Context::kUpdateStateInventoryUpdate = "inventory-update";
211
const string Context::kUpdateStateInventoryUpdateRetryWait = "inventory-update-retry-wait";
212

213
const string Context::kUpdateStateCheckWait = "check-wait";
214
const string Context::kUpdateStateUpdateCheck = "update-check";
215
const string Context::kUpdateStateUpdateFetch = "update-fetch";
216
const string Context::kUpdateStateUpdateAfterStore = "update-after-store";
217
const string Context::kUpdateStateFetchStoreRetryWait = "fetch-install-retry-wait";
218
const string Context::kUpdateStateUpdateVerify = "update-verify";
219
const string Context::kUpdateStateUpdatePreCommitStatusReportRetry = "update-pre-commit-status-report-retry";
220
const string Context::kUpdateStateUpdateStatusReport = "update-status-report";
221
// Would have been used, but a copy/paste error in the Golang client means that it was never
222
// saved. "after-reboot" is stored twice instead.
223
const string Context::kUpdateStateVerifyReboot = "verify-reboot";
224
const string Context::kUpdateStateError = "error";
225
const string Context::kUpdateStateDone = "finished";
226
const string Context::kUpdateStateUpdateControl = "mender-update-control";
227
const string Context::kUpdateStateUpdateControlPause = "mender-update-control-pause";
228
const string Context::kUpdateStateFetchUpdateControl = "mender-update-control-refresh-maps";
229
const string Context::kUpdateStateFetchRetryUpdateControl = "mender-update-control-retry-refresh-maps";
230
#endif
231

232
///////////////////////////////////////////////////////////////////////////////////////////////////
233
// End of database values.
234
///////////////////////////////////////////////////////////////////////////////////////////////////
235

236
static string GenerateStateDataJson(const StateData &state_data) {
632✔
237
        stringstream content;
1,264✔
238

239
        auto append_vector = [&content](const vector<string> &data) {
4,653✔
240
                for (auto entry = data.begin(); entry != data.end(); entry++) {
4,653✔
241
                        if (entry != data.begin()) {
2,125✔
242
                                content << ",";
×
243
                        }
244
                        content << R"(")" << json::EscapeString(*entry) << R"(")";
2,125✔
245
                }
246
        };
2,528✔
247

248
        auto append_map = [&content](const unordered_map<string, string> &data) {
1,200✔
249
                for (auto entry = data.begin(); entry != data.end(); entry++) {
1,200✔
250
                        if (entry != data.begin()) {
568✔
251
                                content << ",";
×
252
                        }
253
                        content << R"(")" << json::EscapeString(entry->first) << R"(":")"
1,136✔
254
                                        << json::EscapeString(entry->second) << R"(")";
1,136✔
255
                }
256
        };
632✔
257

258
        content << "{";
632✔
259
        {
260
                content << R"("Version":)" << to_string(state_data.version) << ",";
632✔
261
                content << R"("Name":")" << json::EscapeString(state_data.state) << R"(",)";
632✔
262
                content << R"("UpdateInfo":{)";
632✔
263
                {
264
                        auto &update_info = state_data.update_info;
632✔
265
                        content << R"("Artifact":{)";
632✔
266
                        {
267
                                auto &artifact = update_info.artifact;
632✔
268
                                content << R"("Source":{)";
632✔
269
                                {
270
                                        content << R"("URI":")" << json::EscapeString(artifact.source.uri) << R"(",)";
632✔
271
                                        content << R"("Expire":")" << json::EscapeString(artifact.source.expire)
632✔
272
                                                        << R"(")";
632✔
273
                                }
274
                                content << "},";
632✔
275

276
                                content << R"("CompatibleDevices":[)";
632✔
277
                                append_vector(artifact.compatible_devices);
632✔
278
                                content << "],";
632✔
279

280
                                content << R"("PayloadTypes":[)";
632✔
281
                                append_vector(artifact.payload_types);
632✔
282
                                content << "],";
632✔
283

284
                                content << R"("ArtifactName":")" << json::EscapeString(artifact.artifact_name)
632✔
285
                                                << R"(",)";
632✔
286
                                content << R"("ArtifactGroup":")" << json::EscapeString(artifact.artifact_group)
632✔
287
                                                << R"(",)";
632✔
288

289
                                content << R"("TypeInfoProvides":{)";
632✔
290
                                append_map(artifact.type_info_provides);
632✔
291
                                content << "},";
632✔
292

293
                                content << R"("ClearsArtifactProvides":[)";
632✔
294
                                append_vector(artifact.clears_artifact_provides);
632✔
295
                                content << "]";
632✔
296
                        }
297
                        content << "},";
632✔
298

299
                        content << R"("ID":")" << json::EscapeString(update_info.id) << R"(",)";
632✔
300

301
                        content << R"("RebootRequested":[)";
632✔
302
                        append_vector(update_info.reboot_requested);
632✔
303
                        content << R"(],)";
632✔
304

305
                        content << R"("SupportsRollback":")"
306
                                        << json::EscapeString(update_info.supports_rollback) << R"(",)";
632✔
307
                        content << R"("StateDataStoreCount":)" << to_string(update_info.state_data_store_count)
632✔
308
                                        << R"(,)";
632✔
309
                        content << R"("HasDBSchemaUpdate":)"
310
                                        << string(update_info.has_db_schema_update ? "true," : "false,");
632✔
311
                        content << R"("AllRollbacksSuccessful":)"
312
                                        << string(update_info.all_rollbacks_successful ? "true" : "false");
632✔
313
                }
314
                content << "}";
632✔
315
        }
316
        content << "}";
632✔
317

318
        return std::move(*content.rdbuf()).str();
1,264✔
319
}
320

321
error::Error Context::SaveDeploymentStateData(kv_db::Transaction &txn, StateData &state_data) {
634✔
322
        if (state_data.update_info.state_data_store_count++ >= kMaxStateDataStoreCount) {
634✔
323
                return main_context::MakeError(
324
                        main_context::StateDataStoreCountExceededError,
325
                        "State looping detected, breaking out of loop");
2✔
326
        }
327

328
        string content = GenerateStateDataJson(state_data);
1,264✔
329

330
        string store_key;
1,264✔
331
        if (state_data.update_info.has_db_schema_update) {
632✔
332
                store_key = mender_context.state_data_key_uncommitted;
4✔
333

334
                // Leave state_data_key alone.
335
        } else {
336
                store_key = mender_context.state_data_key;
628✔
337

338
                auto err = txn.Remove(mender_context.state_data_key_uncommitted);
628✔
339
                if (err != error::NoError) {
628✔
340
                        return err.WithContext("Could not remove uncommitted state data");
×
341
                }
342
        }
343

344
        auto err = txn.Write(store_key, common::ByteVectorFromString(content));
1,264✔
345
        if (err != error::NoError) {
632✔
346
                return err.WithContext("Could not write state data");
×
347
        }
348

349
        return error::NoError;
632✔
350
}
351

352
error::Error Context::SaveDeploymentStateData(StateData &state_data) {
574✔
353
        auto &db = mender_context.GetMenderStoreDB();
574✔
354
        return db.WriteTransaction([this, &state_data](kv_db::Transaction &txn) {
565✔
355
                return SaveDeploymentStateData(txn, state_data);
565✔
356
        });
574✔
357
}
358

359
static error::Error UnmarshalJsonStateData(const json::Json &json, StateData &state_data) {
35✔
360
#define SetOrReturnIfError(dst, expr) \
361
        if (!expr) {                      \
362
                return expr.error();          \
363
        }                                 \
364
        dst = expr.value()
365

366
#define DefaultOrSetOrReturnIfError(dst, expr, def)                            \
367
        if (!expr) {                                                               \
368
                if (expr.error().code == kv_db::MakeError(kv_db::KeyError, "").code) { \
369
                        dst = def;                                                         \
370
                } else {                                                               \
371
                        return expr.error();                                               \
372
                }                                                                      \
373
        } else {                                                                   \
374
                dst = expr.value();                                                    \
375
        }
376

377
        auto exp_int = json.Get("Version").and_then(json::ToInt);
70✔
378
        SetOrReturnIfError(state_data.version, exp_int);
35✔
379

380
        if (state_data.version != kStateDataVersion) {
35✔
381
                return error::Error(
382
                        make_error_condition(errc::not_supported),
1✔
383
                        "State Data version not supported by this client");
3✔
384
        }
385

386
        auto exp_string = json.Get("Name").and_then(json::ToString);
68✔
387
        SetOrReturnIfError(state_data.state, exp_string);
34✔
388

389
        const auto &exp_json_update_info = json.Get("UpdateInfo");
68✔
390
        SetOrReturnIfError(const auto &json_update_info, exp_json_update_info);
34✔
391

392
        const auto &exp_json_artifact = json_update_info.Get("Artifact");
68✔
393
        SetOrReturnIfError(const auto &json_artifact, exp_json_artifact);
34✔
394

395
        const auto &exp_json_source = json_artifact.Get("Source");
68✔
396
        SetOrReturnIfError(const auto &json_source, exp_json_source);
34✔
397

398
        auto &update_info = state_data.update_info;
34✔
399
        auto &artifact = update_info.artifact;
34✔
400
        auto &source = artifact.source;
34✔
401

402
        exp_string = json_source.Get("URI").and_then(json::ToString);
34✔
403
        SetOrReturnIfError(source.uri, exp_string);
34✔
404

405
        exp_string = json_source.Get("Expire").and_then(json::ToString);
34✔
406
        SetOrReturnIfError(source.expire, exp_string);
34✔
407

408
        auto exp_string_vector = json_artifact.Get("CompatibleDevices").and_then(json::ToStringVector);
68✔
409
        SetOrReturnIfError(artifact.compatible_devices, exp_string_vector);
34✔
410

411
        exp_string = json_artifact.Get("ArtifactName").and_then(json::ToString);
34✔
412
        SetOrReturnIfError(artifact.artifact_name, exp_string);
34✔
413

414
        exp_string_vector = json_artifact.Get("PayloadTypes").and_then(json::ToStringVector);
34✔
415
        SetOrReturnIfError(artifact.payload_types, exp_string_vector);
34✔
416
        // It's possible for there not to be an initialized update,
417
        // if the deployment failed before we could successfully parse the artifact.
418
        if (artifact.payload_types.size() == 0 and artifact.artifact_name == "") {
34✔
419
                return error::NoError;
1✔
420
        }
421
        if (artifact.payload_types.size() != 1) {
33✔
422
                return error::Error(
423
                        make_error_condition(errc::not_supported),
×
424
                        "Only exactly one payload type is supported. Got: "
425
                                + to_string(artifact.payload_types.size()));
×
426
        }
427

428
        exp_string = json_artifact.Get("ArtifactGroup").and_then(json::ToString);
33✔
429
        SetOrReturnIfError(artifact.artifact_group, exp_string);
33✔
430

431
        auto exp_string_map = json_artifact.Get("TypeInfoProvides").and_then(json::ToKeyValuesMap);
66✔
432
        DefaultOrSetOrReturnIfError(artifact.type_info_provides, exp_string_map, {});
33✔
433

434
        exp_string_vector = json_artifact.Get("ClearsArtifactProvides").and_then(json::ToStringVector);
33✔
435
        DefaultOrSetOrReturnIfError(artifact.clears_artifact_provides, exp_string_vector, {});
33✔
436

437
        exp_string = json_update_info.Get("ID").and_then(json::ToString);
33✔
438
        SetOrReturnIfError(update_info.id, exp_string);
33✔
439

440
        exp_string_vector = json_update_info.Get("RebootRequested").and_then(json::ToStringVector);
33✔
441
        SetOrReturnIfError(update_info.reboot_requested, exp_string_vector);
33✔
442
        // Check that it's valid strings.
443
        for (const auto &reboot_requested : update_info.reboot_requested) {
57✔
444
                if (reboot_requested != "") {
24✔
445
                        auto exp_needs_reboot = DbStringToNeedsReboot(reboot_requested);
21✔
446
                        if (!exp_needs_reboot) {
21✔
447
                                return exp_needs_reboot.error();
×
448
                        }
449
                }
450
        }
451

452
        exp_string = json_update_info.Get("SupportsRollback").and_then(json::ToString);
33✔
453
        SetOrReturnIfError(update_info.supports_rollback, exp_string);
33✔
454
        // Check that it's a valid string.
455
        if (update_info.supports_rollback != "") {
33✔
456
                auto exp_supports_rollback = DbStringToSupportsRollback(update_info.supports_rollback);
14✔
457
                if (!exp_supports_rollback) {
14✔
458
                        return exp_supports_rollback.error();
×
459
                }
460
        }
461

462
        exp_int = json_update_info.Get("StateDataStoreCount").and_then(json::ToInt);
33✔
463
        SetOrReturnIfError(update_info.state_data_store_count, exp_int);
33✔
464

465
        auto exp_bool = json_update_info.Get("HasDBSchemaUpdate").and_then(json::ToBool);
66✔
466
        SetOrReturnIfError(update_info.has_db_schema_update, exp_bool);
33✔
467

468
        exp_bool = json_update_info.Get("AllRollbacksSuccessful").and_then(json::ToBool);
33✔
469
        DefaultOrSetOrReturnIfError(update_info.all_rollbacks_successful, exp_bool, false);
33✔
470

471
#undef SetOrReturnIfError
472
#undef EmptyOrSetOrReturnIfError
473

474
        return error::NoError;
33✔
475
}
476

477
expected::ExpectedBool Context::LoadDeploymentStateData(StateData &state_data) {
87✔
478
        auto &db = mender_context.GetMenderStoreDB();
87✔
479
        auto err = db.WriteTransaction([this, &state_data](kv_db::Transaction &txn) {
156✔
480
                auto exp_content = txn.Read(mender_context.state_data_key);
172✔
481
                if (!exp_content) {
86✔
482
                        return exp_content.error().WithContext("Could not load state data");
52✔
483
                }
484
                auto &content = exp_content.value();
34✔
485

486
                auto exp_json = json::Load(common::StringFromByteVector(content));
68✔
487
                if (!exp_json) {
34✔
488
                        return exp_json.error().WithContext("Could not load state data");
×
489
                }
490

491
                auto err = UnmarshalJsonStateData(exp_json.value(), state_data);
68✔
492
                if (err != error::NoError) {
34✔
493
                        if (err.code != make_error_condition(errc::not_supported)) {
1✔
494
                                return err.WithContext("Could not load state data");
×
495
                        }
496

497
                        // Try again with the state_data_key_uncommitted.
498
                        exp_content = txn.Read(mender_context.state_data_key_uncommitted);
1✔
499
                        if (!exp_content) {
1✔
500
                                return err.WithContext("Could not load state data").FollowedBy(exp_content.error());
×
501
                        }
502
                        auto &content = exp_content.value();
1✔
503

504
                        exp_json = json::Load(common::StringFromByteVector(content));
1✔
505
                        if (!exp_json) {
1✔
506
                                return err.WithContext("Could not load state data").FollowedBy(exp_json.error());
×
507
                        }
508

509
                        auto inner_err = UnmarshalJsonStateData(exp_json.value(), state_data);
1✔
510
                        if (inner_err != error::NoError) {
1✔
511
                                return err.WithContext("Could not load state data").FollowedBy(inner_err);
×
512
                        }
513

514
                        // Since we loaded from the uncommitted key, set this.
515
                        state_data.update_info.has_db_schema_update = true;
1✔
516
                }
517

518
                // Every load also saves, which increments the state_data_store_count.
519
                return SaveDeploymentStateData(txn, state_data);
34✔
520
        });
174✔
521

522
        if (err == error::NoError) {
87✔
523
                return true;
33✔
524
        } else if (err.code == kv_db::MakeError(kv_db::KeyError, "").code) {
54✔
525
                return false;
52✔
526
        } else {
527
                return expected::unexpected(err);
4✔
528
        }
529
}
530

531
void Context::BeginDeploymentLogging() {
89✔
532
        deployment.logger.reset(new deployments::DeploymentLog(
89✔
533
                mender_context.GetConfig().paths.GetDataStore(), deployment.state_data->update_info.id));
178✔
534
        auto err = deployment.logger->BeginLogging();
178✔
535
        if (err != error::NoError) {
89✔
536
                log::Error(
×
537
                        "Was not able to set up deployment log for deployment ID "
538
                        + deployment.state_data->update_info.id + ": " + err.String());
×
539
                // It's not a fatal error, so continue.
540
        }
541
}
89✔
542

543
void Context::FinishDeploymentLogging() {
89✔
544
        auto err = deployment.logger->FinishLogging();
178✔
545
        if (err != error::NoError) {
89✔
546
                log::Error(
×
547
                        "Was not able to stop deployment log for deployment ID "
548
                        + deployment.state_data->update_info.id + ": " + err.String());
×
549
                // We need to continue regardless
550
        }
551
}
89✔
552

553
} // namespace daemon
554
} // namespace update
555
} // 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