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

mendersoftware / mender / 1010553510

20 Sep 2023 06:54AM UTC coverage: 78.964% (+0.02%) from 78.946%
1010553510

push

gitlab-ci

oleorhagen
feat(bootstrap-artifact): Install the bootstrap Artifact

Ticket: MEN-6671
Changelog: None

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

39 of 39 new or added lines in 3 files covered. (100.0%)

6175 of 7820 relevant lines covered (78.96%)

11616.69 hits per line

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

83.85
/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

22
#include <artifact/v3/scripts/executor.hpp>
23

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

28

29
namespace common = mender::common;
30
namespace events = mender::common::events;
31
namespace executor = mender::artifact::scripts::executor;
32
namespace http = mender::http;
33
namespace io = mender::common::io;
34
namespace log = mender::common::log;
35

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

43
ExpectedOptionalStateData LoadStateData(database::KeyValueDatabase &db) {
53✔
44
        StateDataKeys keys;
45
        StateData dst;
106✔
46

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

57
        auto exp_json = json::Load(common::StringFromByteVector(exp_bytes.value()));
18✔
58
        if (!exp_json) {
9✔
59
                return expected::unexpected(exp_json.error());
×
60
        }
61
        auto &json = exp_json.value();
9✔
62

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

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

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

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

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

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

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

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

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

123
        return dst;
9✔
124
}
125

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

138
error::Error SaveStateData(database::KeyValueDatabase &db, const StateData &data) {
40✔
139
        StateDataKeys keys;
140
        stringstream ss;
80✔
141
        ss << "{";
40✔
142
        ss << "\"" << keys.version << "\":" << data.version;
40✔
143

144
        ss << ",";
40✔
145
        ss << "\"" << keys.artifact_name << "\":\"" << data.artifact_name << "\"";
40✔
146

147
        ss << ",";
40✔
148
        ss << "\"" << keys.artifact_group << "\":\"" << data.artifact_group << "\"";
40✔
149

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

162
        if (data.artifact_provides) {
40✔
163
                ss << ",";
39✔
164
                ss << "\"" << keys.artifact_provides << "\": {";
39✔
165
                bool first = true;
39✔
166
                for (auto elem : data.artifact_provides.value()) {
117✔
167
                        if (!first) {
78✔
168
                                ss << ",";
39✔
169
                        }
170
                        ss << "\"" << elem.first << "\":\"" << elem.second << "\"";
78✔
171
                        first = false;
78✔
172
                }
173
                ss << "}";
39✔
174
        }
175

176
        if (data.artifact_clears_provides) {
40✔
177
                ss << ",";
39✔
178
                ss << "\"" << keys.artifact_clears_provides << "\": [";
39✔
179
                bool first = true;
39✔
180
                for (auto elem : data.artifact_clears_provides.value()) {
156✔
181
                        if (!first) {
117✔
182
                                ss << ",";
78✔
183
                        }
184
                        ss << "\"" << elem << "\"";
117✔
185
                        first = false;
117✔
186
                }
187
                ss << "]";
39✔
188
        }
189

190
        ss << "}";
40✔
191

192
        string strdata = ss.str();
80✔
193
        vector<uint8_t> bytedata(common::ByteVectorFromString(strdata));
80✔
194

195
        return db.Write(context::MenderContext::standalone_state_key, bytedata);
80✔
196
}
197

198
error::Error RemoveStateData(database::KeyValueDatabase &db) {
8✔
199
        return db.Remove(context::MenderContext::standalone_state_key);
8✔
200
}
201

202
static io::ExpectedReaderPtr ReaderFromUrl(
1✔
203
        events::EventLoop &loop, http::Client &http_client, const string &src) {
204
        auto req = make_shared<http::OutgoingRequest>();
2✔
205
        req->SetMethod(http::Method::GET);
1✔
206
        auto err = req->SetAddress(src);
2✔
207
        if (err != error::NoError) {
1✔
208
                return expected::unexpected(err);
×
209
        }
210
        error::Error inner_err;
2✔
211
        io::AsyncReaderPtr reader;
1✔
212
        err = http_client.AsyncCall(
1✔
213
                req,
214
                [&loop, &inner_err, &reader](http::ExpectedIncomingResponsePtr exp_resp) {
2✔
215
                        // No matter what happens, we will want to stop the loop after the headers
216
                        // are received.
217
                        loop.Stop();
1✔
218

219
                        if (!exp_resp) {
1✔
220
                                inner_err = exp_resp.error();
×
221
                                return;
×
222
                        }
223

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

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

233
                        auto exp_reader = resp->MakeBodyAsyncReader();
1✔
234
                        if (!exp_reader) {
1✔
235
                                inner_err = exp_reader.error();
×
236
                                return;
×
237
                        }
238
                        reader = exp_reader.value();
1✔
239
                },
240
                [](http::ExpectedIncomingResponsePtr exp_resp) {
×
241
                        if (!exp_resp) {
×
242
                                log::Warning("While reading HTTP body: " + exp_resp.error().String());
×
243
                        }
244
                });
2✔
245

246
        // Loop until the headers are received. Then we return and let the reader drive the
247
        // rest of the download.
248
        loop.Run();
1✔
249

250
        if (err != error::NoError) {
1✔
251
                return expected::unexpected(err);
×
252
        }
253

254
        if (inner_err != error::NoError) {
1✔
255
                return expected::unexpected(inner_err);
×
256
        }
257

258
        return make_shared<events::io::ReaderFromAsyncReader>(loop, reader);
2✔
259
}
260

261
ResultAndError Install(
43✔
262
        context::MenderContext &main_context,
263
        const string &src,
264
        const artifact::config::Signature verify_signature) {
265
        auto exp_in_progress = LoadStateData(main_context.GetMenderStoreDB());
86✔
266
        if (!exp_in_progress) {
43✔
267
                return {Result::FailedNothingDone, exp_in_progress.error()};
×
268
        }
269
        auto &in_progress = exp_in_progress.value();
43✔
270

271
        if (in_progress) {
43✔
272
                return {
273
                        Result::FailedNothingDone,
274
                        error::Error(
275
                                make_error_condition(errc::operation_in_progress),
1✔
276
                                "Update already in progress. Please commit or roll back first")};
3✔
277
        }
278

279
        io::ReaderPtr artifact_reader;
42✔
280

281
        shared_ptr<events::EventLoop> event_loop;
42✔
282
        http::ClientPtr http_client;
42✔
283

284
        if (src.find("http://") == 0 || src.find("https://") == 0) {
42✔
285
                event_loop = make_shared<events::EventLoop>();
1✔
286
                http::ClientConfig conf;
1✔
287
                http_client = make_shared<http::Client>(conf, *event_loop);
1✔
288
                auto reader = ReaderFromUrl(*event_loop, *http_client, src);
1✔
289
                if (!reader) {
1✔
290
                        return {Result::FailedNothingDone, reader.error()};
×
291
                }
292
                artifact_reader = reader.value();
1✔
293
        } else {
294
                auto stream = io::OpenIfstream(src);
41✔
295
                if (!stream) {
41✔
296
                        return {Result::FailedNothingDone, stream.error()};
×
297
                }
298
                auto file_stream = make_shared<ifstream>(std::move(stream.value()));
41✔
299
                artifact_reader = make_shared<io::StreamReader>(file_stream);
41✔
300
        }
301

302
        artifact::config::ParserConfig config {
42✔
303
                .artifact_scripts_filesystem_path = main_context.GetConfig().paths.GetArtScriptsPath(),
42✔
304
                .artifact_scripts_version = 3,
305
                .verify_signature = verify_signature,
306
        };
84✔
307

308
        auto exp_parser = artifact::Parse(*artifact_reader, config);
84✔
309
        if (!exp_parser) {
42✔
310
                return {Result::FailedNothingDone, exp_parser.error()};
×
311
        }
312
        auto &parser = exp_parser.value();
42✔
313

314
        auto exp_header = artifact::View(parser, 0);
84✔
315
        if (!exp_header) {
42✔
316
                return {Result::FailedNothingDone, exp_header.error()};
×
317
        }
318
        auto &header = exp_header.value();
42✔
319

320
        cout << "Installing artifact..." << endl;
42✔
321

322
        if (header.header.payload_type == "") {
42✔
323
                auto data = StateDataFromPayloadHeaderView(header);
4✔
324
                return DoEmptyPayloadArtifact(main_context, data);
2✔
325
        }
326

327
        update_module::UpdateModule update_module(main_context, header.header.payload_type);
80✔
328

329
        auto err =
330
                update_module.CleanAndPrepareFileTree(update_module.GetUpdateModuleWorkDir(), header);
80✔
331
        if (err != error::NoError) {
40✔
332
                err = err.FollowedBy(update_module.Cleanup());
×
333
                return {Result::FailedNothingDone, err};
×
334
        }
335

336
        StateData data = StateDataFromPayloadHeaderView(header);
80✔
337
        err = SaveStateData(main_context.GetMenderStoreDB(), data);
40✔
338
        if (err != error::NoError) {
40✔
339
                err = err.FollowedBy(update_module.Cleanup());
×
340
                return {Result::FailedNothingDone, err};
×
341
        }
342

343
        return DoInstallStates(main_context, data, parser, update_module);
40✔
344
}
345

346
ResultAndError Commit(context::MenderContext &main_context) {
4✔
347
        auto exp_in_progress = LoadStateData(main_context.GetMenderStoreDB());
8✔
348
        if (!exp_in_progress) {
4✔
349
                return {Result::FailedNothingDone, exp_in_progress.error()};
×
350
        }
351
        auto &in_progress = exp_in_progress.value();
4✔
352

353
        if (!in_progress) {
4✔
354
                return {
355
                        Result::NoUpdateInProgress,
356
                        context::MakeError(context::NoUpdateInProgressError, "Cannot commit")};
2✔
357
        }
358
        auto &data = in_progress.value();
3✔
359

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

362
        if (data.payload_types[0] == "rootfs-image") {
3✔
363
                // Special case for rootfs-image upgrades. See comments inside the function.
364
                auto err = update_module.EnsureRootfsImageFileTree(update_module.GetUpdateModuleWorkDir());
3✔
365
                if (err != error::NoError) {
3✔
366
                        return {Result::FailedNothingDone, err};
×
367
                }
368
        }
369

370
        return DoCommit(main_context, data, update_module);
3✔
371
}
372

373
ResultAndError Rollback(context::MenderContext &main_context) {
6✔
374
        auto exp_in_progress = LoadStateData(main_context.GetMenderStoreDB());
12✔
375
        if (!exp_in_progress) {
6✔
376
                return {Result::FailedNothingDone, exp_in_progress.error()};
×
377
        }
378
        auto &in_progress = exp_in_progress.value();
6✔
379

380
        if (!in_progress) {
6✔
381
                return {
382
                        Result::NoUpdateInProgress,
383
                        context::MakeError(context::NoUpdateInProgressError, "Cannot roll back")};
2✔
384
        }
385
        auto &data = in_progress.value();
5✔
386

387
        update_module::UpdateModule update_module(main_context, data.payload_types[0]);
10✔
388

389
        if (data.payload_types[0] == "rootfs-image") {
5✔
390
                // Special case for rootfs-image upgrades. See comments inside the function.
391
                auto err = update_module.EnsureRootfsImageFileTree(update_module.GetUpdateModuleWorkDir());
5✔
392
                if (err != error::NoError) {
5✔
393
                        return {Result::FailedNothingDone, err};
×
394
                }
395
        }
396

397
        auto result = DoRollback(main_context, data, update_module);
10✔
398

399
        if (result.result == Result::NoRollback) {
5✔
400
                // No support for rollback. Return instead of clearing update data. It should be
401
                // cleared by calling commit or restoring the rollback capability.
402
                return result;
1✔
403
        }
404

405
        auto err = update_module.Cleanup();
8✔
406
        if (err != error::NoError) {
4✔
407
                result.result = Result::FailedAndRollbackFailed;
×
408
                result.err = result.err.FollowedBy(err);
×
409
        }
410

411
        if (result.result == Result::RolledBack) {
4✔
412
                err = RemoveStateData(main_context.GetMenderStoreDB());
3✔
413
        } else {
414
                err = CommitBrokenArtifact(main_context, data);
1✔
415
        }
416
        if (err != error::NoError) {
4✔
417
                result.result = Result::RollbackFailed;
×
418
                result.err = result.err.FollowedBy(err);
×
419
        }
420

421
        return result;
4✔
422
}
423

424
ResultAndError DoInstallStates(
40✔
425
        context::MenderContext &main_context,
426
        StateData &data,
427
        artifact::Artifact &artifact,
428
        update_module::UpdateModule &update_module) {
429
        auto payload = artifact.Next();
80✔
430
        if (!payload) {
40✔
431
                return {Result::FailedNothingDone, payload.error()};
×
432
        }
433

434
        const auto &default_paths {main_context.GetConfig().paths};
40✔
435
        events::EventLoop loop;
80✔
436
        auto script_runner {executor::ScriptRunner(
437
                loop,
438
                chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
40✔
439
                default_paths.GetArtScriptsPath(),
80✔
440
                default_paths.GetRootfsScriptsPath())};
200✔
441

442

443
        // Download Enter
444
        auto err = script_runner.RunScripts(executor::State::Download, executor::Action::Enter);
80✔
445
        if (err != error::NoError) {
40✔
446
                err = err.FollowedBy(
2✔
447
                        script_runner.RunScripts(executor::State::Download, executor::Action::Error));
4✔
448
                err = err.FollowedBy(update_module.Cleanup());
2✔
449
                err = err.FollowedBy(RemoveStateData(main_context.GetMenderStoreDB()));
2✔
450
                return {Result::FailedNothingDone, err};
2✔
451
        }
452
        err = update_module.Download(payload.value());
38✔
453
        if (err != error::NoError) {
38✔
454
                err = err.FollowedBy(update_module.Cleanup());
1✔
455
                err = err.FollowedBy(RemoveStateData(main_context.GetMenderStoreDB()));
1✔
456
                return {Result::FailedNothingDone, err};
1✔
457
        }
458

459
        // Download Leave
460
        err = script_runner.RunScripts(executor::State::Download, executor::Action::Leave);
37✔
461
        if (err != error::NoError) {
37✔
462
                err = script_runner.RunScripts(executor::State::Download, executor::Action::Error);
1✔
463
                err = err.FollowedBy(update_module.Cleanup());
1✔
464
                err = err.FollowedBy(RemoveStateData(main_context.GetMenderStoreDB()));
1✔
465
                return {Result::FailedNothingDone, err};
1✔
466
        }
467

468

469
        // Install Enter
470
        err = script_runner.RunScripts(executor::State::ArtifactInstall, executor::Action::Enter);
36✔
471
        if (err != error::NoError) {
36✔
472
                auto install_leave_error {
473
                        script_runner.RunScripts(executor::State::ArtifactInstall, executor::Action::Error)};
2✔
474
                log::Error(
1✔
475
                        "Failure during Install Enter script execution: "
476
                        + err.FollowedBy(install_leave_error).String());
2✔
477
                return InstallationFailureHandler(main_context, data, update_module);
1✔
478
        }
479

480
        err = update_module.ArtifactInstall();
35✔
481
        if (err != error::NoError) {
35✔
482
                log::Error("Installation failed: " + err.String());
4✔
483
                return InstallationFailureHandler(main_context, data, update_module);
4✔
484
        }
485

486
        // Install Leave
487
        err = script_runner.RunScripts(executor::State::ArtifactInstall, executor::Action::Leave);
31✔
488
        if (err != error::NoError) {
31✔
489
                auto install_leave_error {
490
                        script_runner.RunScripts(executor::State::ArtifactInstall, executor::Action::Error)};
2✔
491

492
                log::Error(
1✔
493
                        "Failure during Install Leave script execution: "
494
                        + err.FollowedBy(install_leave_error).String());
2✔
495

496
                return InstallationFailureHandler(main_context, data, update_module);
1✔
497
        }
498

499
        auto reboot = update_module.NeedsReboot();
60✔
500
        if (!reboot) {
30✔
501
                log::Error("Could not query for reboot: " + reboot.error().String());
×
502
                return InstallationFailureHandler(main_context, data, update_module);
×
503
        }
504

505
        auto rollback_support = update_module.SupportsRollback();
60✔
506
        if (!rollback_support) {
30✔
507
                log::Error("Could not query for rollback support: " + rollback_support.error().String());
×
508
                return InstallationFailureHandler(main_context, data, update_module);
×
509
        }
510

511
        if (rollback_support.value()) {
30✔
512
                if (reboot.value() != update_module::RebootAction::No) {
9✔
513
                        return {Result::InstalledRebootRequired, error::NoError};
×
514
                } else {
515
                        return {Result::Installed, error::NoError};
9✔
516
                }
517
        }
518

519
        cout << "Update Module doesn't support rollback. Committing immediately." << endl;
21✔
520

521
        auto result = DoCommit(main_context, data, update_module);
42✔
522
        if (result.result == Result::Committed) {
21✔
523
                if (reboot.value() != update_module::RebootAction::No) {
16✔
524
                        result.result = Result::InstalledAndCommittedRebootRequired;
2✔
525
                } else {
526
                        result.result = Result::InstalledAndCommitted;
14✔
527
                }
528
        }
529
        return result;
21✔
530
}
531

532
ResultAndError DoCommit(
24✔
533
        context::MenderContext &main_context,
534
        StateData &data,
535
        update_module::UpdateModule &update_module) {
536
        const auto &default_paths {main_context.GetConfig().paths};
24✔
537
        events::EventLoop loop;
48✔
538
        auto script_runner {executor::ScriptRunner(
539
                loop,
540
                chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
24✔
541
                default_paths.GetArtScriptsPath(),
48✔
542
                default_paths.GetRootfsScriptsPath())};
120✔
543
        // Commit Enter
544
        auto err = script_runner.RunScripts(executor::State::ArtifactCommit, executor::Action::Enter);
48✔
545
        if (err != error::NoError) {
24✔
546
                log::Error("Commit Enter State Script error: " + err.String());
3✔
547
                // Commit Error
548
                {
549
                        auto commit_error =
550
                                script_runner.RunScripts(executor::State::ArtifactCommit, executor::Action::Error);
6✔
551
                        if (commit_error != error::NoError) {
3✔
552
                                log::Error("Commit Error State Script error: " + commit_error.String());
×
553
                        }
554
                }
555
                return InstallationFailureHandler(main_context, data, update_module);
3✔
556
        }
557

558
        err = update_module.ArtifactCommit();
21✔
559
        if (err != error::NoError) {
21✔
560
                log::Error("Commit failed: " + err.String());
×
561
                return InstallationFailureHandler(main_context, data, update_module);
×
562
        }
563

564
        auto result = Result::Committed;
21✔
565
        error::Error return_err;
42✔
566

567
        // Commit Leave
568
        err = script_runner.RunScripts(executor::State::ArtifactCommit, executor::Action::Leave);
21✔
569
        if (err != error::NoError) {
21✔
570
                auto leave_err =
571
                        script_runner.RunScripts(executor::State::ArtifactCommit, executor::Action::Error);
1✔
572
                log::Error("Error during Commit Leave script: " + err.FollowedBy(leave_err).String());
1✔
573
                result = Result::InstalledButFailedInPostCommit;
1✔
574
                return_err = err.FollowedBy(leave_err);
1✔
575
        }
576

577

578
        err = update_module.Cleanup();
21✔
579
        if (err != error::NoError) {
21✔
580
                result = Result::InstalledButFailedInPostCommit;
1✔
581
                return_err = return_err.FollowedBy(err);
1✔
582
        }
583

584
        err = main_context.CommitArtifactData(
21✔
585
                data.artifact_name,
21✔
586
                data.artifact_group,
21✔
587
                data.artifact_provides,
21✔
588
                data.artifact_clears_provides,
21✔
589
                [](database::Transaction &txn) {
21✔
590
                        return txn.Remove(context::MenderContext::standalone_state_key);
21✔
591
                });
42✔
592
        if (err != error::NoError) {
21✔
593
                result = Result::InstalledButFailedInPostCommit;
×
594
                return_err = return_err.FollowedBy(err);
×
595
        }
596

597
        return {result, return_err};
21✔
598
}
599

600
ResultAndError DoRollback(
14✔
601
        context::MenderContext &main_context,
602
        StateData &data,
603
        update_module::UpdateModule &update_module) {
604
        auto exp_rollback_support = update_module.SupportsRollback();
28✔
605
        if (!exp_rollback_support) {
14✔
606
                return {Result::NoRollback, exp_rollback_support.error()};
×
607
        }
608

609
        if (exp_rollback_support.value()) {
14✔
610
                auto default_paths {main_context.GetConfig().paths};
14✔
611
                events::EventLoop loop;
14✔
612
                auto script_runner {executor::ScriptRunner(
613
                        loop,
614
                        chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
7✔
615
                        default_paths.GetArtScriptsPath(),
14✔
616
                        default_paths.GetRootfsScriptsPath())};
35✔
617

618
                // Rollback Enter
619
                auto err =
620
                        script_runner.RunScripts(executor::State::ArtifactRollback, executor::Action::Enter);
14✔
621
                if (err != error::NoError) {
7✔
622
                        return {Result::RollbackFailed, err};
×
623
                }
624
                err = update_module.ArtifactRollback();
7✔
625
                if (err != error::NoError) {
7✔
626
                        return {Result::RollbackFailed, err};
2✔
627
                }
628
                // Rollback Leave
629
                err = script_runner.RunScripts(executor::State::ArtifactRollback, executor::Action::Leave);
5✔
630
                if (err != error::NoError) {
5✔
631
                        return {Result::RollbackFailed, err};
×
632
                }
633
                return {Result::RolledBack, error::NoError};
5✔
634
        } else {
635
                return {Result::NoRollback, error::NoError};
7✔
636
        }
637
}
638

639
ResultAndError DoEmptyPayloadArtifact(context::MenderContext &main_context, StateData &data) {
2✔
640
        cout << "Artifact with empty payload. Committing immediately." << endl;
2✔
641

642
        auto err = main_context.CommitArtifactData(
643
                data.artifact_name,
2✔
644
                data.artifact_group,
2✔
645
                data.artifact_provides,
2✔
646
                data.artifact_clears_provides,
2✔
647
                [](database::Transaction &txn) { return error::NoError; });
8✔
648
        if (err != error::NoError) {
2✔
649
                return {Result::InstalledButFailedInPostCommit, err};
×
650
        }
651
        return {Result::InstalledAndCommitted, err};
2✔
652
}
653

654
ResultAndError InstallationFailureHandler(
9✔
655
        context::MenderContext &main_context,
656
        StateData &data,
657
        update_module::UpdateModule &update_module) {
658
        error::Error err;
18✔
659

660
        auto default_paths {main_context.GetConfig().paths};
18✔
661
        events::EventLoop loop;
18✔
662
        auto script_runner {executor::ScriptRunner(
663
                loop,
664
                chrono::seconds {main_context.GetConfig().state_script_timeout_seconds},
9✔
665
                default_paths.GetArtScriptsPath(),
18✔
666
                default_paths.GetRootfsScriptsPath())};
45✔
667

668
        auto result = DoRollback(main_context, data, update_module);
18✔
669
        switch (result.result) {
9✔
670
        case Result::RolledBack:
2✔
671
                result.result = Result::FailedAndRolledBack;
2✔
672
                break;
2✔
673
        case Result::NoRollback:
6✔
674
                result.result = Result::FailedAndNoRollback;
6✔
675
                break;
6✔
676
        case Result::RollbackFailed:
1✔
677
                result.result = Result::FailedAndRollbackFailed;
1✔
678
                break;
1✔
679
        default:
×
680
                // Should not happen.
681
                assert(false);
×
682
                return {
683
                        Result::FailedAndRollbackFailed,
684
                        error::MakeError(
685
                                error::ProgrammingError,
686
                                "Unexpected result in InstallationFailureHandler. This is a bug.")};
687
        }
688

689
        // Failure Enter
690
        err = script_runner.RunScripts(
9✔
691
                executor::State::ArtifactFailure, executor::Action::Enter, executor::RunError::Ignore);
9✔
692
        if (err != error::NoError) {
9✔
693
                log::Error("Failure during execution of ArtifactFailure Enter script: " + err.String());
1✔
694
                result.result = Result::FailedAndRollbackFailed;
1✔
695
                result.err = result.err.FollowedBy(err);
1✔
696
        }
697

698
        err = update_module.ArtifactFailure();
9✔
699
        if (err != error::NoError) {
9✔
700
                result.result = Result::FailedAndRollbackFailed;
1✔
701
                result.err = result.err.FollowedBy(err);
1✔
702
        }
703

704
        // Failure Leave
705
        err = script_runner.RunScripts(
9✔
706
                executor::State::ArtifactFailure, executor::Action::Leave, executor::RunError::Ignore);
9✔
707
        if (err != error::NoError) {
9✔
708
                log::Error("Failure during execution of ArtifactFailure Enter script: " + err.String());
2✔
709
                result.result = Result::FailedAndRollbackFailed;
2✔
710
                result.err = result.err.FollowedBy(err);
2✔
711
        }
712

713
        err = update_module.Cleanup();
9✔
714
        if (err != error::NoError) {
9✔
715
                result.result = Result::FailedAndRollbackFailed;
×
716
                result.err = result.err.FollowedBy(err);
×
717
        }
718

719
        if (result.result == Result::FailedAndRolledBack) {
9✔
720
                err = RemoveStateData(main_context.GetMenderStoreDB());
1✔
721
        } else {
722
                err = CommitBrokenArtifact(main_context, data);
8✔
723
        }
724
        if (err != error::NoError) {
9✔
725
                result.result = Result::FailedAndRollbackFailed;
×
726
                result.err = result.err.FollowedBy(err);
×
727
        }
728

729
        return result;
9✔
730
}
731

732
error::Error CommitBrokenArtifact(context::MenderContext &main_context, StateData &data) {
9✔
733
        data.artifact_name += main_context.broken_artifact_name_suffix;
9✔
734
        if (data.artifact_provides) {
9✔
735
                data.artifact_provides.value()["artifact_name"] = data.artifact_name;
9✔
736
        }
737
        return main_context.CommitArtifactData(
738
                data.artifact_name,
9✔
739
                data.artifact_group,
9✔
740
                data.artifact_provides,
9✔
741
                data.artifact_clears_provides,
9✔
742
                [](database::Transaction &txn) {
9✔
743
                        return txn.Remove(context::MenderContext::standalone_state_key);
9✔
744
                });
9✔
745
}
746

747
} // namespace standalone
748
} // namespace update
749
} // 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