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

mendersoftware / mender / 974575668

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

push

gitlab-ci

kacf
chore: Implement pushing of logs to the server.

Ticket: MEN-6581

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

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

5492 of 6967 relevant lines covered (78.83%)

238.75 hits per line

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

82.11
/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
namespace mender {
23
namespace update {
24
namespace standalone {
25

26
namespace common = mender::common;
27
namespace events = mender::common::events;
28
namespace http = mender::http;
29
namespace io = mender::common::io;
30
namespace log = mender::common::log;
31

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

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

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

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

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

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

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

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

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

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

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

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

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

119
        return dst;
8✔
120
}
121

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

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

140
        ss << ",";
29✔
141
        ss << "\"" << keys.artifact_name << "\":\"" << data.artifact_name << "\"";
29✔
142

143
        ss << ",";
29✔
144
        ss << "\"" << keys.artifact_group << "\":\"" << data.artifact_group << "\"";
29✔
145

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

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

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

186
        ss << "}";
29✔
187

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

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

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

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

215
                        if (!exp_resp) {
1✔
216
                                inner_err = exp_resp.error();
×
217
                                return;
×
218
                        }
219

220
                        auto resp = exp_resp.value();
1✔
221

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

229
                        reader = resp->MakeBodyAsyncReader();
1✔
230
                },
231
                [](http::ExpectedIncomingResponsePtr exp_resp) {
1✔
232
                        if (!exp_resp) {
1✔
233
                                log::Warning("While reading HTTP body: " + exp_resp.error().String());
×
234
                        }
235
                });
3✔
236

237
        // Loop until the headers are received. Then we return and let the reader drive the
238
        // rest of the download.
239
        loop.Run();
1✔
240

241
        if (err != error::NoError) {
1✔
242
                return expected::unexpected(err);
×
243
        }
244

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

249
        return make_shared<events::io::ReaderFromAsyncReader>(loop, reader);
2✔
250
}
251

252
ResultAndError Install(context::MenderContext &main_context, const string &src) {
31✔
253
        auto exp_in_progress = LoadStateData(main_context.GetMenderStoreDB());
62✔
254
        if (!exp_in_progress) {
31✔
255
                return {Result::FailedNothingDone, exp_in_progress.error()};
×
256
        }
257
        auto &in_progress = exp_in_progress.value();
31✔
258

259
        if (in_progress) {
31✔
260
                return {
261
                        Result::FailedNothingDone,
262
                        error::Error(
263
                                make_error_condition(errc::operation_in_progress),
1✔
264
                                "Update already in progress. Please commit or roll back first")};
3✔
265
        }
266

267
        io::ReaderPtr artifact_reader;
30✔
268

269
        shared_ptr<events::EventLoop> event_loop;
30✔
270
        http::ClientPtr http_client;
30✔
271

272
        if (src.find("http://") == 0 || src.find("https://") == 0) {
30✔
273
                event_loop = make_shared<events::EventLoop>();
1✔
274
                http::ClientConfig conf;
1✔
275
                http_client = make_shared<http::Client>(conf, *event_loop);
1✔
276
                auto reader = ReaderFromUrl(*event_loop, *http_client, src);
1✔
277
                if (!reader) {
1✔
278
                        return {Result::FailedNothingDone, reader.error()};
×
279
                }
280
                artifact_reader = reader.value();
1✔
281
        } else {
282
                auto stream = io::OpenIfstream(src);
29✔
283
                if (!stream) {
29✔
284
                        return {Result::FailedNothingDone, stream.error()};
×
285
                }
286
                auto file_stream = make_shared<ifstream>(std::move(stream.value()));
29✔
287
                artifact_reader = make_shared<io::StreamReader>(file_stream);
29✔
288
        }
289

290
        artifact::config::ParserConfig config {
30✔
291
                .artifact_scripts_filesystem_path = main_context.GetConfig().paths.GetArtScriptsPath(),
30✔
292
                .artifact_scripts_version = 3,
293
        };
60✔
294
        auto exp_parser = artifact::Parse(*artifact_reader, config);
60✔
295
        if (!exp_parser) {
30✔
296
                return {Result::FailedNothingDone, exp_parser.error()};
×
297
        }
298
        auto &parser = exp_parser.value();
30✔
299

300
        auto exp_header = artifact::View(parser, 0);
60✔
301
        if (!exp_header) {
30✔
302
                return {Result::FailedNothingDone, exp_header.error()};
×
303
        }
304
        auto &header = exp_header.value();
30✔
305

306
        cout << "Installing artifact..." << endl;
30✔
307

308
        if (header.header.payload_type == "") {
30✔
309
                auto data = StateDataFromPayloadHeaderView(header);
2✔
310
                return DoEmptyPayloadArtifact(main_context, data);
1✔
311
        }
312

313
        update_module::UpdateModule update_module(main_context, header.header.payload_type);
58✔
314

315
        auto err =
316
                update_module.CleanAndPrepareFileTree(update_module.GetUpdateModuleWorkDir(), header);
58✔
317
        if (err != error::NoError) {
29✔
318
                err = err.FollowedBy(update_module.Cleanup());
×
319
                return {Result::FailedNothingDone, err};
×
320
        }
321

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

329
        return DoInstallStates(main_context, data, parser, update_module);
29✔
330
}
331

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

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

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

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

356
        return DoCommit(main_context, data, update_module);
3✔
357
}
358

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

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

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

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

383
        auto result = DoRollback(main_context, data, update_module);
8✔
384

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

391
        auto err = update_module.Cleanup();
6✔
392
        if (err != error::NoError) {
3✔
393
                result.result = Result::FailedAndRollbackFailed;
×
394
                result.err = result.err.FollowedBy(err);
×
395
        }
396

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

407
        return result;
3✔
408
}
409

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

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

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

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

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

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

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

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

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

476
        auto result = Result::Committed;
19✔
477
        error::Error return_err;
38✔
478

479
        err = update_module.Cleanup();
19✔
480
        if (err != error::NoError) {
19✔
481
                result = Result::InstalledButFailedInPostCommit;
1✔
482
                return_err = return_err.FollowedBy(err);
1✔
483
        }
484

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

498
        return {result, return_err};
19✔
499
}
500

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

510
        if (exp_rollback_support.value()) {
8✔
511
                auto err = update_module.ArtifactRollback();
12✔
512
                if (err != error::NoError) {
6✔
513
                        return {Result::RollbackFailed, err};
2✔
514
                }
515

516
                return {Result::RolledBack, error::NoError};
4✔
517
        } else {
518
                return {Result::NoRollback, error::NoError};
2✔
519
        }
520
}
521

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

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

537
ResultAndError InstallationFailureHandler(
4✔
538
        context::MenderContext &main_context,
539
        StateData &data,
540
        update_module::UpdateModule &update_module) {
541
        error::Error err;
8✔
542

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

564
        err = update_module.ArtifactFailure();
4✔
565
        if (err != error::NoError) {
4✔
566
                result.result = Result::FailedAndRollbackFailed;
1✔
567
                result.err = result.err.FollowedBy(err);
1✔
568
        }
569

570
        err = update_module.Cleanup();
4✔
571
        if (err != error::NoError) {
4✔
572
                result.result = Result::FailedAndRollbackFailed;
×
573
                result.err = result.err.FollowedBy(err);
×
574
        }
575

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

586
        return result;
4✔
587
}
588

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

604
} // namespace standalone
605
} // namespace update
606
} // 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