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

mendersoftware / mender / 951615729

pending completion
951615729

push

gitlab-ci

larsewi
fix: Add 'build/' to '.gitignore'

Ticket: None
Changelog: None
Signed-off-by: Lars Erik Wik <lars.erik.wik@northern.tech>

4199 of 5286 relevant lines covered (79.44%)

166.43 hits per line

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

82.16
/mender-update/standalone/standalone.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/standalone.hpp>
16

17
#include <common/common.hpp>
18
#include <common/events_io.hpp>
19
#include <common/http.hpp>
20
#include <common/log.hpp>
21
#include <common/conf/paths.hpp>
22

23
namespace mender {
24
namespace update {
25
namespace standalone {
26

27
namespace common = mender::common;
28
namespace events = mender::common::events;
29
namespace http = mender::http;
30
namespace io = mender::common::io;
31
namespace log = mender::common::log;
32
namespace paths = mender::common::conf::paths;
33

34
const string StateDataKeys::version {"Version"};
35
const string StateDataKeys::artifact_name {"ArtifactName"};
36
const string StateDataKeys::artifact_group {"ArtifactGroup"};
37
const string StateDataKeys::artifact_provides {"ArtifactTypeInfoProvides"};
38
const string StateDataKeys::artifact_clears_provides {"ArtifactClearsProvides"};
39
const string StateDataKeys::payload_types {"PayloadTypes"};
40

41
ExpectedOptionalStateData LoadStateData(database::KeyValueDatabase &db) {
40✔
42
        StateDataKeys keys;
43
        StateData dst;
80✔
44

45
        auto exp_bytes = db.Read(context::MenderContext::standalone_state_key);
80✔
46
        if (!exp_bytes) {
40✔
47
                auto &err = exp_bytes.error();
32✔
48
                if (err.code == database::MakeError(database::KeyError, "").code) {
32✔
49
                        return optional::optional<StateData>();
64✔
50
                } else {
51
                        return expected::unexpected(err);
×
52
                }
53
        }
54

55
        auto exp_json = json::Load(common::StringFromByteVector(exp_bytes.value()));
16✔
56
        if (!exp_json) {
8✔
57
                return expected::unexpected(exp_json.error());
×
58
        }
59
        auto &json = exp_json.value();
8✔
60

61
        auto exp_int = json::Get<int64_t>(json, keys.version, json::MissingOk::No);
16✔
62
        if (!exp_int) {
8✔
63
                return expected::unexpected(exp_int.error());
×
64
        }
65
        dst.version = exp_int.value();
8✔
66

67
        auto exp_string = json::Get<string>(json, keys.artifact_name, json::MissingOk::No);
16✔
68
        if (!exp_string) {
8✔
69
                return expected::unexpected(exp_string.error());
×
70
        }
71
        dst.artifact_name = exp_string.value();
8✔
72

73
        exp_string = json::Get<string>(json, keys.artifact_group, json::MissingOk::Yes);
8✔
74
        if (!exp_string) {
8✔
75
                return expected::unexpected(exp_string.error());
×
76
        }
77
        dst.artifact_group = exp_string.value();
8✔
78

79
        auto exp_map = json::Get<json::KeyValueMap>(json, keys.artifact_provides, json::MissingOk::No);
16✔
80
        if (exp_map) {
8✔
81
                dst.artifact_provides = exp_map.value();
7✔
82
        } else {
83
                dst.artifact_provides.reset();
1✔
84
        }
85

86
        auto exp_array =
87
                json::Get<vector<string>>(json, keys.artifact_clears_provides, json::MissingOk::No);
16✔
88
        if (exp_array) {
8✔
89
                dst.artifact_clears_provides = exp_array.value();
7✔
90
        } else {
91
                dst.artifact_clears_provides.reset();
1✔
92
        }
93

94
        exp_array = json::Get<vector<string>>(json, keys.payload_types, json::MissingOk::No);
8✔
95
        if (!exp_array) {
8✔
96
                return expected::unexpected(exp_array.error());
×
97
        }
98
        dst.payload_types = exp_array.value();
8✔
99

100
        if (dst.version != context::MenderContext::standalone_data_version) {
8✔
101
                return expected::unexpected(error::Error(
×
102
                        make_error_condition(errc::not_supported),
×
103
                        "State data has a version which is not supported by this client"));
×
104
        }
105

106
        if (dst.artifact_name == "") {
8✔
107
                return expected::unexpected(context::MakeError(
×
108
                        context::DatabaseValueError, "`" + keys.artifact_name + "` is empty"));
×
109
        }
110

111
        if (dst.payload_types.size() == 0) {
8✔
112
                return expected::unexpected(context::MakeError(
×
113
                        context::DatabaseValueError, "`" + keys.payload_types + "` is empty"));
×
114
        }
115
        if (dst.payload_types.size() >= 2) {
8✔
116
                return expected::unexpected(error::Error(
×
117
                        make_error_condition(errc::not_supported),
×
118
                        "`" + keys.payload_types + "` contains multiple payloads"));
×
119
        }
120

121
        return dst;
8✔
122
}
123

124
StateData StateDataFromPayloadHeaderView(const artifact::PayloadHeaderView &header) {
30✔
125
        StateData dst;
30✔
126
        dst.version = context::MenderContext::standalone_data_version;
30✔
127
        dst.artifact_name = header.header.artifact_name;
30✔
128
        dst.artifact_group = header.header.artifact_group;
30✔
129
        dst.artifact_provides = header.header.type_info.artifact_provides;
30✔
130
        dst.artifact_clears_provides = header.header.type_info.clears_artifact_provides;
30✔
131
        dst.payload_types.clear();
30✔
132
        dst.payload_types.push_back(header.header.payload_type);
30✔
133
        return dst;
30✔
134
}
135

136
error::Error SaveStateData(database::KeyValueDatabase &db, const StateData &data) {
29✔
137
        StateDataKeys keys;
138
        stringstream ss;
58✔
139
        ss << "{";
29✔
140
        ss << "\"" << keys.version << "\":" << data.version;
29✔
141

142
        ss << ",";
29✔
143
        ss << "\"" << keys.artifact_name << "\":\"" << data.artifact_name << "\"";
29✔
144

145
        ss << ",";
29✔
146
        ss << "\"" << keys.artifact_group << "\":\"" << data.artifact_group << "\"";
29✔
147

148
        ss << ",";
29✔
149
        ss << "\"" << keys.payload_types << "\": [";
29✔
150
        bool first = true;
29✔
151
        for (auto elem : data.payload_types) {
58✔
152
                if (!first) {
29✔
153
                        ss << ",";
×
154
                }
155
                ss << "\"" << elem << "\"";
29✔
156
                first = false;
29✔
157
        }
158
        ss << "]";
29✔
159

160
        if (data.artifact_provides) {
29✔
161
                ss << ",";
28✔
162
                ss << "\"" << keys.artifact_provides << "\": {";
28✔
163
                bool first = true;
28✔
164
                for (auto elem : data.artifact_provides.value()) {
84✔
165
                        if (!first) {
56✔
166
                                ss << ",";
28✔
167
                        }
168
                        ss << "\"" << elem.first << "\":\"" << elem.second << "\"";
56✔
169
                        first = false;
56✔
170
                }
171
                ss << "}";
28✔
172
        }
173

174
        if (data.artifact_clears_provides) {
29✔
175
                ss << ",";
28✔
176
                ss << "\"" << keys.artifact_clears_provides << "\": [";
28✔
177
                bool first = true;
28✔
178
                for (auto elem : data.artifact_clears_provides.value()) {
112✔
179
                        if (!first) {
84✔
180
                                ss << ",";
56✔
181
                        }
182
                        ss << "\"" << elem << "\"";
84✔
183
                        first = false;
84✔
184
                }
185
                ss << "]";
28✔
186
        }
187

188
        ss << "}";
29✔
189

190
        string strdata = ss.str();
58✔
191
        vector<uint8_t> bytedata(common::ByteVectorFromString(strdata));
58✔
192

193
        return db.Write(context::MenderContext::standalone_state_key, bytedata);
58✔
194
}
195

196
error::Error RemoveStateData(database::KeyValueDatabase &db) {
4✔
197
        return db.Remove(context::MenderContext::standalone_state_key);
4✔
198
}
199

200
static expected::expected<pair<http::ClientPtr, io::ReaderPtr>, error::Error>
201
ClientAndReaderFromUrl(const string &src) {
1✔
202
        http::ClientPtr http_client;
1✔
203
        auto maybe_artifact_reader = events::io::ReaderFromAsyncReader::Construct(
204
                [&http_client, &src](events::EventLoop &loop) -> io::ExpectedAsyncReaderPtr {
4✔
205
                        http::ClientConfig conf;
2✔
206
                        http_client = make_shared<http::Client>(conf, loop);
1✔
207
                        auto req = make_shared<http::OutgoingRequest>();
2✔
208
                        req->SetMethod(http::Method::GET);
1✔
209
                        auto err = req->SetAddress(src);
2✔
210
                        if (err != error::NoError) {
1✔
211
                                return expected::unexpected(err);
×
212
                        }
213
                        error::Error inner_err;
2✔
214
                        io::AsyncReaderPtr reader;
1✔
215
                        err = http_client->AsyncCall(
2✔
216
                                req,
217
                                [&loop, &inner_err, &reader](http::ExpectedIncomingResponsePtr exp_resp) {
3✔
218
                                        if (!exp_resp) {
1✔
219
                                                inner_err = exp_resp.error();
×
220
                                                return;
×
221
                                        }
222

223
                                        auto resp = exp_resp.value();
1✔
224

225
                                        if (resp->GetStatusCode() != http::StatusOK) {
1✔
226
                                                inner_err = context::MakeError(
×
227
                                                        context::UnexpectedHttpResponse,
228
                                                        to_string(resp->GetStatusCode()) + ": " + resp->GetStatusMessage());
×
229
                                                return;
×
230
                                        }
231

232
                                        reader = resp->MakeBodyAsyncReader();
1✔
233

234
                                        loop.Stop();
1✔
235
                                },
236
                                [](http::ExpectedIncomingResponsePtr exp_resp) {
1✔
237
                                        if (!exp_resp) {
1✔
238
                                                log::Warning("While reading HTTP body: " + exp_resp.error().String());
×
239
                                        }
240
                                });
3✔
241

242
                        // Loop until the headers are received. Then we return and let the reader drive the
243
                        // rest of the download.
244
                        loop.Run();
1✔
245

246
                        if (inner_err != error::NoError) {
1✔
247
                                return expected::unexpected(inner_err);
×
248
                        }
249

250
                        return reader;
1✔
251
                });
2✔
252

253
        if (!maybe_artifact_reader) {
1✔
254
                return expected::unexpected(maybe_artifact_reader.error());
×
255
        }
256

257
        return pair<http::ClientPtr, io::ReaderPtr> {http_client, maybe_artifact_reader.value()};
2✔
258
}
259

260
ResultAndError Install(context::MenderContext &main_context, const string &src) {
31✔
261
        auto exp_in_progress = LoadStateData(main_context.GetMenderStoreDB());
62✔
262
        if (!exp_in_progress) {
31✔
263
                return {Result::FailedNothingDone, exp_in_progress.error()};
×
264
        }
265
        auto &in_progress = exp_in_progress.value();
31✔
266

267
        if (in_progress) {
31✔
268
                return {
269
                        Result::FailedNothingDone,
270
                        error::Error(
271
                                make_error_condition(errc::operation_in_progress),
1✔
272
                                "Update already in progress. Please commit or roll back first")};
3✔
273
        }
274

275
        io::ReaderPtr artifact_reader;
30✔
276

277
        http::ClientPtr http_client;
30✔
278

279
        if (src.find("http://") == 0 || src.find("https://") == 0) {
30✔
280
                auto client_and_reader = ClientAndReaderFromUrl(src);
1✔
281
                if (!client_and_reader) {
1✔
282
                        return {Result::FailedNothingDone, client_and_reader.error()};
×
283
                }
284
                http_client = client_and_reader.value().first;
1✔
285
                artifact_reader = client_and_reader.value().second;
1✔
286
        } else {
287
                auto stream = io::OpenIfstream(src);
29✔
288
                if (!stream) {
29✔
289
                        return {Result::FailedNothingDone, stream.error()};
×
290
                }
291
                auto file_stream = make_shared<ifstream>(std::move(stream.value()));
29✔
292
                artifact_reader = make_shared<io::StreamReader>(file_stream);
29✔
293
        }
294

295
        artifact::config::ParserConfig config {
296
                paths::DefaultArtScriptsPath,
297
        };
60✔
298
        auto exp_parser = artifact::Parse(*artifact_reader, config);
60✔
299
        if (!exp_parser) {
30✔
300
                return {Result::FailedNothingDone, exp_parser.error()};
×
301
        }
302
        auto &parser = exp_parser.value();
30✔
303

304
        auto exp_header = artifact::View(parser, 0);
60✔
305
        if (!exp_header) {
30✔
306
                return {Result::FailedNothingDone, exp_header.error()};
×
307
        }
308
        auto &header = exp_header.value();
30✔
309

310
        cout << "Installing artifact..." << endl;
30✔
311

312
        if (header.header.payload_type == "") {
30✔
313
                auto data = StateDataFromPayloadHeaderView(header);
2✔
314
                return DoEmptyPayloadArtifact(main_context, data);
1✔
315
        }
316

317
        update_module::UpdateModule update_module(main_context, header.header.payload_type);
58✔
318

319
        auto err =
320
                update_module.CleanAndPrepareFileTree(update_module.GetUpdateModuleWorkDir(), header);
58✔
321
        if (err != error::NoError) {
29✔
322
                err = err.FollowedBy(update_module.Cleanup());
×
323
                return {Result::FailedNothingDone, err};
×
324
        }
325

326
        StateData data = StateDataFromPayloadHeaderView(header);
58✔
327
        err = SaveStateData(main_context.GetMenderStoreDB(), data);
29✔
328
        if (err != error::NoError) {
29✔
329
                err = err.FollowedBy(update_module.Cleanup());
×
330
                return {Result::FailedNothingDone, err};
×
331
        }
332

333
        return DoInstallStates(main_context, data, parser, update_module);
29✔
334
}
335

336
ResultAndError Commit(context::MenderContext &main_context) {
4✔
337
        auto exp_in_progress = LoadStateData(main_context.GetMenderStoreDB());
8✔
338
        if (!exp_in_progress) {
4✔
339
                return {Result::FailedNothingDone, exp_in_progress.error()};
×
340
        }
341
        auto &in_progress = exp_in_progress.value();
4✔
342

343
        if (!in_progress) {
4✔
344
                return {
345
                        Result::NoUpdateInProgress,
346
                        context::MakeError(context::NoUpdateInProgressError, "Cannot commit")};
2✔
347
        }
348
        auto &data = in_progress.value();
3✔
349

350
        update_module::UpdateModule update_module(main_context, data.payload_types[0]);
6✔
351

352
        if (data.payload_types[0] == "rootfs-image") {
3✔
353
                // Special case for rootfs-image upgrades. See comments inside the function.
354
                auto err = update_module.EnsureRootfsImageFileTree(update_module.GetUpdateModuleWorkDir());
3✔
355
                if (err != error::NoError) {
3✔
356
                        return {Result::FailedNothingDone, err};
×
357
                }
358
        }
359

360
        return DoCommit(main_context, data, update_module);
3✔
361
}
362

363
ResultAndError Rollback(context::MenderContext &main_context) {
5✔
364
        auto exp_in_progress = LoadStateData(main_context.GetMenderStoreDB());
10✔
365
        if (!exp_in_progress) {
5✔
366
                return {Result::FailedNothingDone, exp_in_progress.error()};
×
367
        }
368
        auto &in_progress = exp_in_progress.value();
5✔
369

370
        if (!in_progress) {
5✔
371
                return {
372
                        Result::NoUpdateInProgress,
373
                        context::MakeError(context::NoUpdateInProgressError, "Cannot roll back")};
2✔
374
        }
375
        auto &data = in_progress.value();
4✔
376

377
        update_module::UpdateModule update_module(main_context, data.payload_types[0]);
8✔
378

379
        if (data.payload_types[0] == "rootfs-image") {
4✔
380
                // Special case for rootfs-image upgrades. See comments inside the function.
381
                auto err = update_module.EnsureRootfsImageFileTree(update_module.GetUpdateModuleWorkDir());
4✔
382
                if (err != error::NoError) {
4✔
383
                        return {Result::FailedNothingDone, err};
×
384
                }
385
        }
386

387
        auto result = DoRollback(main_context, data, update_module);
8✔
388

389
        if (result.result == Result::NoRollback) {
4✔
390
                // No support for rollback. Return instead of clearing update data. It should be
391
                // cleared by calling commit or restoring the rollback capability.
392
                return result;
1✔
393
        }
394

395
        auto err = update_module.Cleanup();
6✔
396
        if (err != error::NoError) {
3✔
397
                result.result = Result::FailedAndRollbackFailed;
×
398
                result.err = result.err.FollowedBy(err);
×
399
        }
400

401
        if (result.result == Result::RolledBack) {
3✔
402
                err = RemoveStateData(main_context.GetMenderStoreDB());
2✔
403
        } else {
404
                err = CommitBrokenArtifact(main_context, data);
1✔
405
        }
406
        if (err != error::NoError) {
3✔
407
                result.result = Result::RollbackFailed;
×
408
                result.err = result.err.FollowedBy(err);
×
409
        }
410

411
        return result;
3✔
412
}
413

414
ResultAndError DoInstallStates(
29✔
415
        context::MenderContext &main_context,
416
        StateData &data,
417
        artifact::Artifact &artifact,
418
        update_module::UpdateModule &update_module) {
419
        auto payload = artifact.Next();
58✔
420
        if (!payload) {
29✔
421
                return {Result::FailedNothingDone, payload.error()};
×
422
        }
423

424
        auto err = update_module.Download(payload.value());
58✔
425
        if (err != error::NoError) {
29✔
426
                err = err.FollowedBy(update_module.Cleanup());
1✔
427
                err = err.FollowedBy(RemoveStateData(main_context.GetMenderStoreDB()));
1✔
428
                return {Result::FailedNothingDone, err};
1✔
429
        }
430

431
        err = update_module.ArtifactInstall();
28✔
432
        if (err != error::NoError) {
28✔
433
                log::Error("Installation failed: " + err.String());
4✔
434
                return InstallationFailureHandler(main_context, data, update_module);
4✔
435
        }
436

437
        auto reboot = update_module.NeedsReboot();
48✔
438
        if (!reboot) {
24✔
439
                log::Error("Could not query for reboot: " + reboot.error().String());
×
440
                return InstallationFailureHandler(main_context, data, update_module);
×
441
        }
442

443
        auto rollback_support = update_module.SupportsRollback();
48✔
444
        if (!rollback_support) {
24✔
445
                log::Error("Could not query for rollback support: " + rollback_support.error().String());
×
446
                return InstallationFailureHandler(main_context, data, update_module);
×
447
        }
448

449
        if (rollback_support.value()) {
24✔
450
                if (reboot.value() != update_module::RebootAction::No) {
8✔
451
                        return {Result::InstalledRebootRequired, error::NoError};
×
452
                } else {
453
                        return {Result::Installed, error::NoError};
8✔
454
                }
455
        }
456

457
        cout << "Update Module doesn't support rollback. Committing immediately." << endl;
16✔
458

459
        auto result = DoCommit(main_context, data, update_module);
32✔
460
        if (result.result == Result::Committed) {
16✔
461
                if (reboot.value() != update_module::RebootAction::No) {
15✔
462
                        result.result = Result::InstalledAndCommittedRebootRequired;
2✔
463
                } else {
464
                        result.result = Result::InstalledAndCommitted;
13✔
465
                }
466
        }
467
        return result;
16✔
468
}
469

470
ResultAndError DoCommit(
19✔
471
        context::MenderContext &main_context,
472
        StateData &data,
473
        update_module::UpdateModule &update_module) {
474
        auto err = update_module.ArtifactCommit();
38✔
475
        if (err != error::NoError) {
19✔
476
                log::Error("Commit failed: " + err.String());
×
477
                return InstallationFailureHandler(main_context, data, update_module);
×
478
        }
479

480
        auto result = Result::Committed;
19✔
481
        error::Error return_err;
38✔
482

483
        err = update_module.Cleanup();
19✔
484
        if (err != error::NoError) {
19✔
485
                result = Result::InstalledButFailedInPostCommit;
1✔
486
                return_err = return_err.FollowedBy(err);
1✔
487
        }
488

489
        err = main_context.CommitArtifactData(
19✔
490
                data.artifact_name,
19✔
491
                data.artifact_group,
19✔
492
                data.artifact_provides,
19✔
493
                data.artifact_clears_provides,
19✔
494
                [](database::Transaction &txn) {
19✔
495
                        return txn.Remove(context::MenderContext::standalone_state_key);
19✔
496
                });
38✔
497
        if (err != error::NoError) {
19✔
498
                result = Result::InstalledButFailedInPostCommit;
×
499
                return_err = return_err.FollowedBy(err);
×
500
        }
501

502
        return {result, return_err};
19✔
503
}
504

505
ResultAndError DoRollback(
8✔
506
        context::MenderContext &main_context,
507
        StateData &data,
508
        update_module::UpdateModule &update_module) {
509
        auto exp_rollback_support = update_module.SupportsRollback();
16✔
510
        if (!exp_rollback_support) {
8✔
511
                return {Result::NoRollback, exp_rollback_support.error()};
×
512
        }
513

514
        if (exp_rollback_support.value()) {
8✔
515
                auto err = update_module.ArtifactRollback();
12✔
516
                if (err != error::NoError) {
6✔
517
                        return {Result::RollbackFailed, err};
2✔
518
                }
519

520
                return {Result::RolledBack, error::NoError};
4✔
521
        } else {
522
                return {Result::NoRollback, error::NoError};
2✔
523
        }
524
}
525

526
ResultAndError DoEmptyPayloadArtifact(context::MenderContext &main_context, StateData &data) {
1✔
527
        cout << "Artifact with empty payload. Committing immediately." << endl;
1✔
528

529
        auto err = main_context.CommitArtifactData(
530
                data.artifact_name,
1✔
531
                data.artifact_group,
1✔
532
                data.artifact_provides,
1✔
533
                data.artifact_clears_provides,
1✔
534
                [](database::Transaction &txn) { return error::NoError; });
4✔
535
        if (err != error::NoError) {
1✔
536
                return {Result::InstalledButFailedInPostCommit, err};
×
537
        }
538
        return {Result::InstalledAndCommitted, err};
1✔
539
}
540

541
ResultAndError InstallationFailureHandler(
4✔
542
        context::MenderContext &main_context,
543
        StateData &data,
544
        update_module::UpdateModule &update_module) {
545
        error::Error err;
8✔
546

547
        auto result = DoRollback(main_context, data, update_module);
8✔
548
        switch (result.result) {
4✔
549
        case Result::RolledBack:
2✔
550
                result.result = Result::FailedAndRolledBack;
2✔
551
                break;
2✔
552
        case Result::NoRollback:
1✔
553
                result.result = Result::FailedAndNoRollback;
1✔
554
                break;
1✔
555
        case Result::RollbackFailed:
1✔
556
                result.result = Result::FailedAndRollbackFailed;
1✔
557
                break;
1✔
558
        default:
×
559
                // Should not happen.
560
                assert(false);
×
561
                return {
562
                        Result::FailedAndRollbackFailed,
563
                        error::MakeError(
564
                                error::ProgrammingError,
565
                                "Unexpected result in InstallationFailureHandler. This is a bug.")};
566
        }
567

568
        err = update_module.ArtifactFailure();
4✔
569
        if (err != error::NoError) {
4✔
570
                result.result = Result::FailedAndRollbackFailed;
1✔
571
                result.err = result.err.FollowedBy(err);
1✔
572
        }
573

574
        err = update_module.Cleanup();
4✔
575
        if (err != error::NoError) {
4✔
576
                result.result = Result::FailedAndRollbackFailed;
×
577
                result.err = result.err.FollowedBy(err);
×
578
        }
579

580
        if (result.result == Result::FailedAndRolledBack) {
4✔
581
                err = RemoveStateData(main_context.GetMenderStoreDB());
1✔
582
        } else {
583
                err = CommitBrokenArtifact(main_context, data);
3✔
584
        }
585
        if (err != error::NoError) {
4✔
586
                result.result = Result::FailedAndRollbackFailed;
×
587
                result.err = result.err.FollowedBy(err);
×
588
        }
589

590
        return result;
4✔
591
}
592

593
error::Error CommitBrokenArtifact(context::MenderContext &main_context, StateData &data) {
4✔
594
        data.artifact_name += main_context.broken_artifact_name_suffix;
4✔
595
        if (data.artifact_provides) {
4✔
596
                data.artifact_provides.value()["artifact_name"] = data.artifact_name;
4✔
597
        }
598
        return main_context.CommitArtifactData(
599
                data.artifact_name,
4✔
600
                data.artifact_group,
4✔
601
                data.artifact_provides,
4✔
602
                data.artifact_clears_provides,
4✔
603
                [](database::Transaction &txn) {
4✔
604
                        return txn.Remove(context::MenderContext::standalone_state_key);
4✔
605
                });
4✔
606
}
607

608
} // namespace standalone
609
} // namespace update
610
} // 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