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

mendersoftware / mender / 1006302423

15 Sep 2023 12:56PM UTC coverage: 78.737% (+0.3%) from 78.482%
1006302423

push

gitlab-ci

oleorhagen
feat(daemon): Add state script support

Ticket: MEN-6637
Changelog: None

Signed-off-by: Ole Petter <ole.orhagen@northern.tech>

194 of 194 new or added lines in 4 files covered. (100.0%)

5984 of 7600 relevant lines covered (78.74%)

1109.89 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) {
52✔
38
        StateData data;
104✔
39

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

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

57
        str = json.Get("artifact")
52✔
58
                          .and_then([](const json::Json &json) { return json.Get("source"); })
156✔
59
                          .and_then([](const json::Json &json) { return json.Get("expire"); })
156✔
60
                          .and_then(json::ToString);
52✔
61
        if (str) {
52✔
62
                data.update_info.artifact.source.expire = str.value();
52✔
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;
52✔
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) {
67✔
101
        switch (action) {
67✔
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:
59✔
107
                return Context::kRebootTypeCustom;
59✔
108
        default:
×
109
                // Should not happen.
110
                assert(false);
×
111
                return Context::kRebootTypeNone;
112
        }
113
}
114

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

148
Context::Context(main_context::MenderContext &mender_context, events::EventLoop &event_loop) :
87✔
149
        mender_context(mender_context),
150
        event_loop(event_loop),
151
        authenticator(
152
                event_loop,
153
                http::ClientConfig(mender_context.GetConfig().server_certificate),
174✔
154
                mender_context.GetConfig().server_url,
87✔
155
                mender_context.GetConfig().paths.GetKeyFile(),
174✔
156
                mender_context.GetConfig().paths.GetIdentityScript()),
174✔
157
        http_client(
158
                http::ClientConfig(mender_context.GetConfig().server_certificate),
174✔
159
                event_loop,
160
                authenticator),
87✔
161
        download_client(http::ClientConfig(mender_context.GetConfig().server_certificate), event_loop),
174✔
162
        deployment_client(make_shared<deployments::DeploymentClient>()),
87✔
163
        inventory_client(make_shared<inventory::InventoryClient>()) {
696✔
164
}
87✔
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) {
605✔
237
        stringstream content;
1,210✔
238

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

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

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

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

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

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

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

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

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

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

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

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

321
error::Error Context::SaveDeploymentStateData(kv_db::Transaction &txn, StateData &state_data) {
607✔
322
        if (state_data.update_info.state_data_store_count++ >= kMaxStateDataStoreCount) {
607✔
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,210✔
329

330
        string store_key;
1,210✔
331
        if (state_data.update_info.has_db_schema_update) {
605✔
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;
601✔
337

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

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

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

352
error::Error Context::SaveDeploymentStateData(StateData &state_data) {
550✔
353
        auto &db = mender_context.GetMenderStoreDB();
550✔
354
        return db.WriteTransaction([this, &state_data](kv_db::Transaction &txn) {
541✔
355
                return SaveDeploymentStateData(txn, state_data);
541✔
356
        });
550✔
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) {
84✔
478
        auto &db = mender_context.GetMenderStoreDB();
84✔
479
        auto err = db.WriteTransaction([this, &state_data](kv_db::Transaction &txn) {
153✔
480
                auto exp_content = txn.Read(mender_context.state_data_key);
166✔
481
                if (!exp_content) {
83✔
482
                        return exp_content.error().WithContext("Could not load state data");
49✔
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
        });
168✔
521

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

531
void Context::BeginDeploymentLogging() {
86✔
532
        deployment.logger.reset(new deployments::DeploymentLog(
86✔
533
                mender_context.GetConfig().paths.GetDataStore(), deployment.state_data->update_info.id));
172✔
534
        auto err = deployment.logger->BeginLogging();
172✔
535
        if (err != error::NoError) {
86✔
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
}
86✔
542

543
void Context::FinishDeploymentLogging() {
86✔
544
        auto err = deployment.logger->FinishLogging();
172✔
545
        if (err != error::NoError) {
86✔
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
}
86✔
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