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

mendersoftware / mender / 974575668

21 Aug 2023 12:04PM UTC coverage: 78.829% (-0.05%) from 78.877%
974575668

push

gitlab-ci

kacf
chore: Implement pushing of logs to the server.

Ticket: MEN-6581

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

18 of 18 new or added lines in 2 files covered. (100.0%)

5492 of 6967 relevant lines covered (78.83%)

238.75 hits per line

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

88.19
/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) {
30✔
38
        StateData data;
60✔
39

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

46
        str = json.Get("artifact")
30✔
47
                          .and_then([](const json::Json &json) { return json.Get("source"); })
90✔
48
                          .and_then([](const json::Json &json) { return json.Get("uri"); })
90✔
49
                          .and_then(json::ToString);
30✔
50
        if (!str) {
30✔
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();
30✔
55
        log::Debug("Artifact Download URL: " + data.update_info.artifact.source.uri);
30✔
56

57
        str = json.Get("artifact")
30✔
58
                          .and_then([](const json::Json &json) { return json.Get("source"); })
90✔
59
                          .and_then([](const json::Json &json) { return json.Get("expire"); })
90✔
60
                          .and_then(json::ToString);
30✔
61
        if (str) {
30✔
62
                data.update_info.artifact.source.expire = str.value();
30✔
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;
30✔
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) {
22✔
80
        return support ? Context::kRollbackSupported : Context::kRollbackNotSupported;
22✔
81
}
82

83
expected::ExpectedBool DbStringToSupportsRollback(const string &str) {
8✔
84
        if (str == Context::kRollbackSupported) {
8✔
85
                return true;
6✔
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) {
34✔
101
        switch (action) {
34✔
102
        case update_module::RebootAction::No:
4✔
103
                return Context::kRebootTypeNone;
4✔
104
        case update_module::RebootAction::Automatic:
×
105
                return Context::kRebootTypeAutomatic;
×
106
        case update_module::RebootAction::Yes:
30✔
107
                return Context::kRebootTypeCustom;
30✔
108
        default:
×
109
                // Should not happen.
110
                assert(false);
×
111
                return Context::kRebootTypeNone;
112
        }
113
}
114

115
update_module::ExpectedRebootAction DbStringToNeedsReboot(const string &str) {
24✔
116
        if (str == Context::kRebootTypeNone) {
24✔
117
                return update_module::RebootAction::No;
×
118
        } else if (str == Context::kRebootTypeAutomatic) {
24✔
119
                return update_module::RebootAction::Automatic;
×
120
        } else if (str == Context::kRebootTypeCustom) {
24✔
121
                return update_module::RebootAction::Yes;
24✔
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) {
26✔
130
        auto &artifact = update_info.artifact;
26✔
131
        auto &header = view.header;
26✔
132
        artifact.compatible_devices = header.header_info.depends.device_type;
26✔
133
        artifact.payload_types = {header.payload_type};
52✔
134
        artifact.artifact_name = header.artifact_name;
26✔
135
        artifact.artifact_group = header.artifact_group;
26✔
136
        if (header.type_info.artifact_provides) {
26✔
137
                artifact.type_info_provides = header.type_info.artifact_provides.value();
25✔
138
        } else {
139
                artifact.type_info_provides.clear();
1✔
140
        }
141
        if (header.type_info.clears_artifact_provides) {
26✔
142
                artifact.clears_artifact_provides = header.type_info.clears_artifact_provides.value();
25✔
143
        } else {
144
                artifact.clears_artifact_provides.clear();
1✔
145
        }
146
}
26✔
147

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

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

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

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

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

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

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

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

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

235
static string GenerateStateDataJson(const StateData &state_data) {
261✔
236
        stringstream content;
522✔
237

238
        auto append_vector = [&content](const vector<string> &data) {
1,994✔
239
                for (auto entry = data.begin(); entry != data.end(); entry++) {
1,994✔
240
                        if (entry != data.begin()) {
950✔
241
                                content << ",";
×
242
                        }
243
                        content << R"(")" << json::EscapeString(*entry) << R"(")";
950✔
244
                }
245
        };
1,044✔
246

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

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

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

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

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

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

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

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

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

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

317
        return std::move(*content.rdbuf()).str();
522✔
318
}
319

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

327
        string content = GenerateStateDataJson(state_data);
522✔
328

329
        string store_key;
522✔
330
        if (state_data.update_info.has_db_schema_update) {
261✔
331
                store_key = mender_context.state_data_key_uncommitted;
4✔
332

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

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

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

348
        return error::NoError;
261✔
349
}
350

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

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

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

376
        auto exp_int = json.Get("Version").and_then(json::ToInt);
34✔
377
        SetOrReturnIfError(state_data.version, exp_int);
17✔
378

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

385
        auto exp_string = json.Get("Name").and_then(json::ToString);
32✔
386
        SetOrReturnIfError(state_data.state, exp_string);
16✔
387

388
        const auto &exp_json_update_info = json.Get("UpdateInfo");
32✔
389
        SetOrReturnIfError(const auto &json_update_info, exp_json_update_info);
16✔
390

391
        const auto &exp_json_artifact = json_update_info.Get("Artifact");
32✔
392
        SetOrReturnIfError(const auto &json_artifact, exp_json_artifact);
16✔
393

394
        const auto &exp_json_source = json_artifact.Get("Source");
32✔
395
        SetOrReturnIfError(const auto &json_source, exp_json_source);
16✔
396

397
        auto &update_info = state_data.update_info;
16✔
398
        auto &artifact = update_info.artifact;
16✔
399
        auto &source = artifact.source;
16✔
400

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

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

407
        auto exp_string_vector = json_artifact.Get("CompatibleDevices").and_then(json::ToStringVector);
32✔
408
        SetOrReturnIfError(artifact.compatible_devices, exp_string_vector);
16✔
409

410
        exp_string_vector = json_artifact.Get("PayloadTypes").and_then(json::ToStringVector);
16✔
411
        SetOrReturnIfError(artifact.payload_types, exp_string_vector);
16✔
412
        if (artifact.payload_types.size() != 1) {
16✔
413
                return error::Error(
414
                        make_error_condition(errc::not_supported),
×
415
                        "Only exactly one payload type is supported");
×
416
        }
417

418
        exp_string = json_artifact.Get("ArtifactName").and_then(json::ToString);
16✔
419
        SetOrReturnIfError(artifact.artifact_name, exp_string);
16✔
420

421
        exp_string = json_artifact.Get("ArtifactGroup").and_then(json::ToString);
16✔
422
        SetOrReturnIfError(artifact.artifact_group, exp_string);
16✔
423

424
        auto exp_string_map = json_artifact.Get("TypeInfoProvides").and_then(json::ToKeyValuesMap);
32✔
425
        DefaultOrSetOrReturnIfError(artifact.type_info_provides, exp_string_map, {});
16✔
426

427
        exp_string_vector = json_artifact.Get("ClearsArtifactProvides").and_then(json::ToStringVector);
16✔
428
        DefaultOrSetOrReturnIfError(artifact.clears_artifact_provides, exp_string_vector, {});
16✔
429

430
        exp_string = json_update_info.Get("ID").and_then(json::ToString);
16✔
431
        SetOrReturnIfError(update_info.id, exp_string);
16✔
432

433
        exp_string_vector = json_update_info.Get("RebootRequested").and_then(json::ToStringVector);
16✔
434
        SetOrReturnIfError(update_info.reboot_requested, exp_string_vector);
16✔
435
        // Check that it's valid strings.
436
        for (const auto &reboot_requested : update_info.reboot_requested) {
28✔
437
                if (reboot_requested != "") {
12✔
438
                        auto exp_needs_reboot = DbStringToNeedsReboot(reboot_requested);
11✔
439
                        if (!exp_needs_reboot) {
11✔
440
                                return exp_needs_reboot.error();
×
441
                        }
442
                }
443
        }
444

445
        exp_string = json_update_info.Get("SupportsRollback").and_then(json::ToString);
16✔
446
        SetOrReturnIfError(update_info.supports_rollback, exp_string);
16✔
447
        // Check that it's a valid string.
448
        if (update_info.supports_rollback != "") {
16✔
449
                auto exp_supports_rollback = DbStringToSupportsRollback(update_info.supports_rollback);
8✔
450
                if (!exp_supports_rollback) {
8✔
451
                        return exp_supports_rollback.error();
×
452
                }
453
        }
454

455
        exp_int = json_update_info.Get("StateDataStoreCount").and_then(json::ToInt);
16✔
456
        SetOrReturnIfError(update_info.state_data_store_count, exp_int);
16✔
457

458
        auto exp_bool = json_update_info.Get("HasDBSchemaUpdate").and_then(json::ToBool);
32✔
459
        SetOrReturnIfError(update_info.has_db_schema_update, exp_bool);
16✔
460

461
        exp_bool = json_update_info.Get("AllRollbacksSuccessful").and_then(json::ToBool);
16✔
462
        DefaultOrSetOrReturnIfError(update_info.all_rollbacks_successful, exp_bool, false);
16✔
463

464
#undef SetOrReturnIfError
465
#undef EmptyOrSetOrReturnIfError
466

467
        return error::NoError;
16✔
468
}
469

470
expected::ExpectedBool Context::LoadDeploymentStateData(StateData &state_data) {
44✔
471
        auto &db = mender_context.GetMenderStoreDB();
44✔
472
        auto err = db.WriteTransaction([this, &state_data](kv_db::Transaction &txn) {
77✔
473
                auto exp_content = txn.Read(mender_context.state_data_key);
86✔
474
                if (!exp_content) {
43✔
475
                        return exp_content.error().WithContext("Could not load state data");
27✔
476
                }
477
                auto &content = exp_content.value();
16✔
478

479
                auto exp_json = json::Load(common::StringFromByteVector(content));
32✔
480
                if (!exp_json) {
16✔
481
                        return exp_json.error().WithContext("Could not load state data");
×
482
                }
483

484
                auto err = UnmarshalJsonStateData(exp_json.value(), state_data);
32✔
485
                if (err != error::NoError) {
16✔
486
                        if (err.code != make_error_condition(errc::not_supported)) {
1✔
487
                                return err.WithContext("Could not load state data");
×
488
                        }
489

490
                        // Try again with the state_data_key_uncommitted.
491
                        exp_content = txn.Read(mender_context.state_data_key_uncommitted);
1✔
492
                        if (!exp_content) {
1✔
493
                                return err.WithContext("Could not load state data").FollowedBy(exp_content.error());
×
494
                        }
495
                        auto &content = exp_content.value();
1✔
496

497
                        exp_json = json::Load(common::StringFromByteVector(content));
1✔
498
                        if (!exp_json) {
1✔
499
                                return err.WithContext("Could not load state data").FollowedBy(exp_json.error());
×
500
                        }
501

502
                        auto inner_err = UnmarshalJsonStateData(exp_json.value(), state_data);
1✔
503
                        if (inner_err != error::NoError) {
1✔
504
                                return err.WithContext("Could not load state data").FollowedBy(inner_err);
×
505
                        }
506

507
                        // Since we loaded from the uncommitted key, set this.
508
                        state_data.update_info.has_db_schema_update = true;
1✔
509
                }
510

511
                // Every load also saves, which increments the state_data_store_count.
512
                return SaveDeploymentStateData(txn, state_data);
16✔
513
        });
88✔
514

515
        if (err == error::NoError) {
44✔
516
                return true;
15✔
517
        } else if (err.code == kv_db::MakeError(kv_db::KeyError, "").code) {
29✔
518
                return false;
27✔
519
        } else {
520
                return expected::unexpected(err);
4✔
521
        }
522
}
523

524
void Context::BeginDeploymentLogging() {
46✔
525
        deployment.logger.reset(new deployments::DeploymentLog(
46✔
526
                mender_context.GetConfig().paths.GetDataStore(), deployment.state_data->update_info.id));
92✔
527
        auto err = deployment.logger->BeginLogging();
92✔
528
        if (err != error::NoError) {
46✔
529
                log::Error(
×
530
                        "Was not able to set up deployment log for deployment ID "
531
                        + deployment.state_data->update_info.id + ": " + err.String());
×
532
                // It's not a fatal error, so continue.
533
        }
534
}
46✔
535

536
void Context::FinishDeploymentLogging() {
46✔
537
        auto err = deployment.logger->FinishLogging();
92✔
538
        if (err != error::NoError) {
46✔
539
                log::Error(
×
540
                        "Was not able to stop deployment log for deployment ID "
541
                        + deployment.state_data->update_info.id + ": " + err.String());
×
542
                // We need to continue regardless
543
        }
544
}
46✔
545

546
} // namespace daemon
547
} // namespace update
548
} // 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