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

mendersoftware / mender / 1487964749

09 Oct 2024 09:17AM UTC coverage: 76.332% (+0.05%) from 76.281%
1487964749

push

gitlab-ci

kacf
chore: Increase robustness of `--stop-before flag.

First and foremost, make sure that repeated resuming before the same
state, with the same `--stop-before` flag, is a noop. It should keep
stopping there. Also make sure that once we have started executing the
path after that, it should no longer be possible to go back.

For this we introduce some new DB flags, but since the existing ones
have not been released yet, this should be fine.

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

94 of 96 new or added lines in 2 files covered. (97.92%)

170 existing lines in 4 files now uncovered.

7308 of 9574 relevant lines covered (76.33%)

11298.84 hits per line

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

79.04
/src/mender-update/standalone/states.cpp
1
// Copyright 2024 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/states.hpp>
16

17
#include <common/http.hpp>
18
#include <common/events_io.hpp>
19
#include <common/io.hpp>
20
#include <common/key_value_database.hpp>
21
#include <common/log.hpp>
22
#include <common/path.hpp>
23

24
#include <mender-update/standalone.hpp>
25

26
namespace mender {
27
namespace update {
28
namespace standalone {
29

30
namespace database = mender::common::key_value_database;
31
namespace events = mender::common::events;
32
namespace http = mender::common::http;
33
namespace io = mender::common::io;
34
namespace log = mender::common::log;
35
namespace path = mender::common::path;
36

37
// This is used to catch mistakes where we don't set the error before exiting the state machine.
38
static const error::Error kFallbackError = error::MakeError(
39
        error::ProgrammingError, "Returned from standalone operation without setting error code.");
40

41
static void UpdateResult(ResultAndError &result, const ResultAndError &update) {
280✔
42
        if (result.err == kFallbackError or result.err == error::NoError) {
280✔
43
                result.err = update.err;
234✔
44
        } else {
45
                result.err = result.err.FollowedBy(update.err);
92✔
46
        }
47

48
        result.result = result.result | update.result;
280✔
49
}
280✔
50

51
void SaveState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
386✔
52
        ctx.state_data.in_state = state_;
386✔
53

54
        if (ResultContains(ctx.result_and_error.result, Result::Failed)) {
386✔
55
                ctx.state_data.failed = true;
35✔
56
        }
57
        if (ResultContains(ctx.result_and_error.result, Result::RolledBack)
58
                or ResultContains(ctx.result_and_error.result, Result::NoRollbackNecessary)) {
386✔
59
                ctx.state_data.rolled_back = true;
23✔
60
        }
61
        if (ResultContains(ctx.result_and_error.result, Result::RollbackFailed)) {
386✔
62
                ctx.state_data.rolled_back = false;
9✔
63
        }
64

65
        auto err = SaveStateData(ctx.main_context.GetMenderStoreDB(), ctx.state_data);
386✔
66
        if (err != error::NoError) {
386✔
67
                UpdateResult(ctx.result_and_error, {Result::Failed, err});
×
68
                poster.PostEvent(StateEvent::Failure);
×
69
                return;
70
        }
71

72
        OnEnterSaveState(ctx, poster);
386✔
73
}
74

75
void JustSaveState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
342✔
76
        // Nothing other than saving, which has already happened.
77
        poster.PostEvent(StateEvent::Success);
342✔
78
}
342✔
79

80
error::Error DoEmptyPayloadArtifact(Context &ctx) {
4✔
81
        if (ctx.options != InstallOptions::NoStdout) {
4✔
82
                cout << "Installing artifact..." << endl;
1✔
83
                cout << "Artifact with empty payload. Committing immediately." << endl;
1✔
84
        }
85

86
        auto &data = ctx.state_data;
87
        return ctx.main_context.CommitArtifactData(
4✔
88
                data.artifact_name,
4✔
89
                data.artifact_group,
4✔
90
                data.artifact_provides,
4✔
91
                data.artifact_clears_provides,
4✔
92
                [](database::Transaction &txn) { return error::NoError; });
12✔
93
}
94

95
static io::ExpectedReaderPtr ReaderFromUrl(
1✔
96
        events::EventLoop &loop, http::Client &http_client, const string &src) {
97
        auto req = make_shared<http::OutgoingRequest>();
1✔
98
        req->SetMethod(http::Method::GET);
1✔
99
        auto err = req->SetAddress(src);
1✔
100
        if (err != error::NoError) {
1✔
UNCOV
101
                return expected::unexpected(err);
×
102
        }
103
        error::Error inner_err;
1✔
104
        io::AsyncReaderPtr reader;
1✔
105
        err = http_client.AsyncCall(
1✔
106
                req,
107
                [&loop, &inner_err, &reader](http::ExpectedIncomingResponsePtr exp_resp) {
1✔
108
                        // No matter what happens, we will want to stop the loop after the headers
109
                        // are received.
110
                        loop.Stop();
1✔
111

112
                        if (!exp_resp) {
1✔
UNCOV
113
                                inner_err = exp_resp.error();
×
UNCOV
114
                                return;
×
115
                        }
116

117
                        auto resp = exp_resp.value();
1✔
118

119
                        if (resp->GetStatusCode() != http::StatusOK) {
1✔
UNCOV
120
                                inner_err = context::MakeError(
×
121
                                        context::UnexpectedHttpResponse,
UNCOV
122
                                        to_string(resp->GetStatusCode()) + ": " + resp->GetStatusMessage());
×
UNCOV
123
                                return;
×
124
                        }
125

126
                        auto exp_reader = resp->MakeBodyAsyncReader();
1✔
127
                        if (!exp_reader) {
1✔
128
                                inner_err = exp_reader.error();
×
129
                                return;
130
                        }
131
                        reader = exp_reader.value();
1✔
132
                },
133
                [](http::ExpectedIncomingResponsePtr exp_resp) {
1✔
134
                        // Note: Since we stop the event loop above, this handler will not be called
135
                        // while we are inside the `ReaderFromUrl` stack frame. It will be called
136
                        // later though, when the reader that we return has finished reading
137
                        // everything (which includes resuming the loop). So be careful with
138
                        // captures in this handler.
139
                        if (!exp_resp) {
1✔
UNCOV
140
                                log::Warning("While reading HTTP body: " + exp_resp.error().String());
×
141
                        }
142
                });
4✔
143

144
        // Loop until the headers are received. Then we return and let the reader drive the
145
        // rest of the download.
146
        loop.Run();
1✔
147

148
        if (err != error::NoError) {
1✔
UNCOV
149
                return expected::unexpected(err);
×
150
        }
151

152
        if (inner_err != error::NoError) {
1✔
UNCOV
153
                return expected::unexpected(inner_err);
×
154
        }
155

156
        // Should not happen since we have checked both `err` and `inner_err`, but just to be safe.
157
        AssertOrReturnUnexpected(reader != nullptr);
1✔
158

159
        return make_shared<events::io::ReaderFromAsyncReader>(loop, reader);
2✔
160
}
161

162
StateData StateDataFromPayloadHeaderView(const artifact::PayloadHeaderView &header) {
62✔
163
        StateData dst;
62✔
164
        dst.version = context::MenderContext::standalone_data_version;
62✔
165
        dst.artifact_name = header.header.artifact_name;
62✔
166
        dst.artifact_group = header.header.artifact_group;
62✔
167
        dst.artifact_provides = header.header.type_info.artifact_provides;
168
        dst.artifact_clears_provides = header.header.type_info.clears_artifact_provides;
169
        dst.payload_types.clear();
62✔
170
        dst.payload_types.push_back(header.header.payload_type);
62✔
171
        return dst;
62✔
172
}
173

174
void PrepareDownloadState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
62✔
175
        auto &main_context = ctx.main_context;
62✔
176

177
        if (ctx.artifact_src.find("http://") == 0 || ctx.artifact_src.find("https://") == 0) {
62✔
178
                ctx.http_client =
179
                        make_shared<http::Client>(main_context.GetConfig().GetHttpClientConfig(), ctx.loop);
2✔
180
                auto reader = ReaderFromUrl(ctx.loop, *ctx.http_client, ctx.artifact_src);
1✔
181
                if (!reader) {
1✔
UNCOV
182
                        UpdateResult(
×
UNCOV
183
                                ctx.result_and_error,
×
184
                                {Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary,
185
                                 reader.error()});
UNCOV
186
                        poster.PostEvent(StateEvent::Failure);
×
187
                        return;
188
                }
189
                ctx.artifact_reader = reader.value();
1✔
190
        } else {
191
                auto stream = io::OpenIfstream(ctx.artifact_src);
61✔
192
                if (!stream) {
61✔
UNCOV
193
                        UpdateResult(
×
UNCOV
194
                                ctx.result_and_error,
×
195
                                {Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary,
196
                                 stream.error()});
UNCOV
197
                        poster.PostEvent(StateEvent::Failure);
×
198
                        return;
199
                }
200
                auto file_stream = make_shared<ifstream>(std::move(stream.value()));
61✔
201
                ctx.artifact_reader = make_shared<io::StreamReader>(file_stream);
122✔
202
        }
203

204
        string art_scripts_path = main_context.GetConfig().paths.GetArtScriptsPath();
205

206
        // Clear the artifact scripts directory so we don't risk old scripts lingering.
207
        auto err = path::DeleteRecursively(art_scripts_path);
62✔
208
        if (err != error::NoError) {
62✔
UNCOV
209
                UpdateResult(
×
UNCOV
210
                        ctx.result_and_error,
×
211
                        {Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary,
UNCOV
212
                         err.WithContext("When preparing to parse artifact")});
×
UNCOV
213
                poster.PostEvent(StateEvent::Failure);
×
214
                return;
215
        }
216

217
        artifact::config::ParserConfig config {
62✔
218
                .artifact_scripts_filesystem_path = main_context.GetConfig().paths.GetArtScriptsPath(),
219
                .artifact_scripts_version = 3,
220
                .artifact_verify_keys = main_context.GetConfig().artifact_verify_keys,
62✔
221
                .verify_signature = ctx.verify_signature,
62✔
222
        };
119✔
223

224
        auto exp_parser = artifact::Parse(*ctx.artifact_reader, config);
124✔
225
        if (!exp_parser) {
62✔
UNCOV
226
                UpdateResult(
×
UNCOV
227
                        ctx.result_and_error,
×
228
                        {Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary,
229
                         exp_parser.error()});
UNCOV
230
                poster.PostEvent(StateEvent::Failure);
×
231
                return;
232
        }
233
        ctx.parser.reset(new artifact::Artifact(std::move(exp_parser.value())));
62✔
234

235
        auto exp_header = artifact::View(*ctx.parser, 0);
62✔
236
        if (!exp_header) {
62✔
UNCOV
237
                UpdateResult(
×
UNCOV
238
                        ctx.result_and_error,
×
239
                        {Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary,
240
                         exp_header.error()});
UNCOV
241
                poster.PostEvent(StateEvent::Failure);
×
242
                return;
243
        }
244
        auto &header = exp_header.value();
62✔
245

246
        ctx.state_data = StateDataFromPayloadHeaderView(header);
62✔
247

248
        if (header.header.payload_type == "") {
62✔
249
                err = DoEmptyPayloadArtifact(ctx);
4✔
250
                if (err != error::NoError) {
4✔
UNCOV
251
                        UpdateResult(
×
UNCOV
252
                                ctx.result_and_error,
×
253
                                {Result::DownloadFailed | Result::Failed | Result::FailedInPostCommit, err});
UNCOV
254
                        poster.PostEvent(StateEvent::Failure);
×
255
                        return;
256
                }
257
                UpdateResult(
4✔
258
                        ctx.result_and_error,
4✔
259
                        {Result::Downloaded | Result::Installed | Result::Committed, error::NoError});
260
                poster.PostEvent(StateEvent::EmptyPayloadArtifact);
4✔
261
                return;
262
        }
263

264
        ctx.update_module.reset(
265
                new update_module::UpdateModule(main_context, header.header.payload_type));
58✔
266

267
        err = ctx.update_module->CleanAndPrepareFileTree(
58✔
268
                ctx.update_module->GetUpdateModuleWorkDir(), header);
58✔
269
        if (err != error::NoError) {
58✔
UNCOV
270
                UpdateResult(
×
UNCOV
271
                        ctx.result_and_error,
×
272
                        {Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary, err});
UNCOV
273
                poster.PostEvent(StateEvent::Failure);
×
274
                return;
275
        }
276

277
        if (ctx.options != InstallOptions::NoStdout) {
58✔
278
                cout << "Installing artifact..." << endl;
58✔
279
        }
280

281
        auto exp_matches = main_context.MatchesArtifactDepends(header.header);
58✔
282
        if (!exp_matches) {
58✔
UNCOV
283
                UpdateResult(
×
UNCOV
284
                        ctx.result_and_error,
×
285
                        {Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary,
286
                         exp_matches.error()});
UNCOV
287
                poster.PostEvent(StateEvent::Failure);
×
288
                return;
289
        } else if (!exp_matches.value()) {
58✔
290
                // reasons already logged
291
                UpdateResult(
1✔
292
                        ctx.result_and_error,
1✔
293
                        {Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary,
294
                         error::NoError});
295
                poster.PostEvent(StateEvent::Failure);
1✔
296
                return;
297
        }
298

299
        poster.PostEvent(StateEvent::Success);
57✔
300
}
301

302
error::Error DoDownloadState(Context &ctx) {
55✔
303
        auto payload = ctx.parser->Next();
55✔
304
        if (!payload) {
55✔
UNCOV
305
                return payload.error();
×
306
        }
307

308
        // ProvidePayloadFileSizes
309
        auto with_sizes = ctx.update_module->ProvidePayloadFileSizes();
55✔
310
        if (!with_sizes) {
55✔
UNCOV
311
                log::Error("Could not query for provide file sizes: " + with_sizes.error().String());
×
UNCOV
312
                return with_sizes.error();
×
313
        }
314

315
        error::Error err;
55✔
316
        if (with_sizes.value()) {
55✔
317
                err = ctx.update_module->DownloadWithFileSizes(payload.value());
2✔
318
        } else {
319
                err = ctx.update_module->Download(payload.value());
108✔
320
        }
321
        if (err != error::NoError) {
55✔
322
                return err;
2✔
323
        }
324

325
        payload = ctx.parser->Next();
106✔
326
        if (payload) {
53✔
UNCOV
327
                err = error::Error(
×
UNCOV
328
                        make_error_condition(errc::not_supported),
×
UNCOV
329
                        "Multiple payloads are not supported in standalone mode");
×
330
        } else if (
53✔
331
                payload.error().code
332
                != artifact::parser_error::MakeError(artifact::parser_error::EOFError, "").code) {
53✔
333
                err = payload.error();
×
334
        }
335
        if (err != error::NoError) {
53✔
UNCOV
336
                return err;
×
337
        }
338

339
        return error::NoError;
53✔
340
}
341

342
void DownloadState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
55✔
343
        auto err = DoDownloadState(ctx);
55✔
344
        if (err != error::NoError) {
55✔
345
                log::Error("Streaming failed: " + err.String());
4✔
346
                UpdateResult(
2✔
347
                        ctx.result_and_error,
2✔
348
                        {Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary, err});
349
                poster.PostEvent(StateEvent::Failure);
2✔
350
                return;
351
        }
352

353
        UpdateResult(ctx.result_and_error, {Result::Downloaded, error::NoError});
53✔
354
        poster.PostEvent(StateEvent::Success);
53✔
355
}
356

357
void ArtifactInstallState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
51✔
358
        auto err = ctx.update_module->ArtifactInstall();
51✔
359
        if (err != error::NoError) {
51✔
360
                log::Error("Installation failed: " + err.String());
12✔
361
                UpdateResult(ctx.result_and_error, {Result::InstallFailed | Result::Failed, err});
6✔
362
                poster.PostEvent(StateEvent::Failure);
6✔
363
                return;
364
        }
365

366
        UpdateResult(ctx.result_and_error, {Result::Installed, error::NoError});
45✔
367
        poster.PostEvent(StateEvent::Success);
45✔
368
}
369

370
RebootAndRollbackQueryState::RebootAndRollbackQueryState() :
87✔
371
        SaveState(StateData::kBeforeStateArtifactCommit_Enter) {
174✔
372
}
87✔
373

374
void RebootAndRollbackQueryState::OnEnterSaveState(
44✔
375
        Context &ctx, sm::EventPoster<StateEvent> &poster) {
376
        auto reboot = ctx.update_module->NeedsReboot();
44✔
377
        if (!reboot) {
44✔
UNCOV
378
                log::Error("Could not query for reboot: " + reboot.error().String());
×
UNCOV
379
                UpdateResult(ctx.result_and_error, {Result::Failed, reboot.error()});
×
UNCOV
380
                poster.PostEvent(StateEvent::Failure);
×
381
                return;
382
        }
383

384
        if (reboot.value() != update_module::RebootAction::No) {
44✔
385
                UpdateResult(ctx.result_and_error, {Result::RebootRequired, error::NoError});
4✔
386
        }
387

388
        auto rollback_support = ctx.update_module->SupportsRollback();
44✔
389
        if (!rollback_support) {
44✔
390
                log::Error("Could not query for rollback support: " + rollback_support.error().String());
×
UNCOV
391
                UpdateResult(ctx.result_and_error, {Result::Failed, rollback_support.error()});
×
UNCOV
392
                poster.PostEvent(StateEvent::Failure);
×
393
                return;
394
        }
395

396
        if (rollback_support.value()) {
44✔
397
                poster.PostEvent(StateEvent::NeedsInteraction);
11✔
398
                return;
399
        } else {
400
                UpdateResult(ctx.result_and_error, {Result::AutoCommitWanted});
33✔
401
        }
402

403
        poster.PostEvent(StateEvent::Success);
33✔
404
}
405

406
void ArtifactCommitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
35✔
407
        auto err = ctx.update_module->ArtifactCommit();
35✔
408
        if (err != error::NoError) {
35✔
409
                log::Error("Commit failed: " + err.String());
2✔
410
                UpdateResult(ctx.result_and_error, {Result::CommitFailed | Result::Failed, err});
1✔
411
                poster.PostEvent(StateEvent::Failure);
1✔
412
                return;
413
        }
414

415
        UpdateResult(ctx.result_and_error, {Result::Committed, error::NoError});
34✔
416
        poster.PostEvent(StateEvent::Success);
34✔
417
}
418

419
void RollbackQueryState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
19✔
420
        auto rollback_support = ctx.update_module->SupportsRollback();
19✔
421
        if (!rollback_support) {
19✔
UNCOV
422
                log::Error("Could not query for rollback support: " + rollback_support.error().String());
×
UNCOV
423
                UpdateResult(
×
UNCOV
424
                        ctx.result_and_error,
×
425
                        {Result::Failed | Result::RollbackFailed, rollback_support.error()});
UNCOV
426
                poster.PostEvent(StateEvent::Failure);
×
427
                return;
428
        }
429

430
        if (not rollback_support.value()) {
19✔
431
                bool already_failed = ResultContains(ctx.result_and_error.result, Result::Failed);
9✔
432
                UpdateResult(ctx.result_and_error, {Result::Failed | Result::NoRollback, error::NoError});
9✔
433
                if (already_failed) {
9✔
434
                        poster.PostEvent(StateEvent::NothingToDo);
8✔
435
                } else {
436
                        // If it hadn't failed already, it's because the user asked for the rollback
437
                        // explicitly. In this case bail out instead of continuing.
438
                        poster.PostEvent(StateEvent::NeedsInteraction);
1✔
439
                }
440
                return;
9✔
441
        }
442

443
        poster.PostEvent(StateEvent::Success);
10✔
444
}
445

446
void ArtifactRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
9✔
447
        auto err = ctx.update_module->ArtifactRollback();
9✔
448
        if (err != error::NoError) {
9✔
449
                UpdateResult(ctx.result_and_error, {Result::Failed | Result::RollbackFailed, err});
2✔
450
                poster.PostEvent(StateEvent::Failure);
2✔
451
                return;
452
        }
453

454
        UpdateResult(ctx.result_and_error, {Result::RolledBack, error::NoError});
7✔
455
        poster.PostEvent(StateEvent::Success);
7✔
456
}
457

458
void ArtifactFailureState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
17✔
459
        auto err = ctx.update_module->ArtifactFailure();
17✔
460
        if (err != error::NoError) {
17✔
461
                UpdateResult(ctx.result_and_error, {Result::Failed | Result::RollbackFailed, err});
1✔
462
                poster.PostEvent(StateEvent::Failure);
1✔
463
                return;
464
        }
465

466
        poster.PostEvent(StateEvent::Success);
16✔
467
}
468

469
void CleanupState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
55✔
470
        auto final_event = StateEvent::Success;
471

472
        auto &data = ctx.state_data;
473

474
        // If this is null, then it is simply a no-op, the update did not even get started.
475
        if (ctx.update_module != nullptr) {
55✔
476
                auto err = ctx.update_module->Cleanup();
55✔
477
                if (err != error::NoError) {
55✔
478
                        UpdateResult(ctx.result_and_error, {Result::Failed | Result::CleanupFailed, err});
1✔
479
                        final_event = StateEvent::Failure;
480
                        data.failed = true;
1✔
481
                        // Fall through so that we update the DB.
482
                }
483
        }
484

485
        error::Error err;
55✔
486
        if (data.rolled_back) {
55✔
487
                // Successful rollback.
488
                auto &db = ctx.main_context.GetMenderStoreDB();
11✔
489
                err = db.Remove(context::MenderContext::standalone_state_key);
22✔
490
        } else {
491
                if (data.failed) {
44✔
492
                        // Unsuccessful rollback or missing rollback support.
493
                        data.artifact_name += ctx.main_context.broken_artifact_name_suffix;
14✔
494
                        if (data.artifact_provides) {
14✔
495
                                data.artifact_provides.value()["artifact_name"] = data.artifact_name;
28✔
496
                        }
497
                        // Fall through to success case.
498
                }
499
                // Commit artifact data and remove state data
500
                err = ctx.main_context.CommitArtifactData(
88✔
501
                        data.artifact_name,
44✔
502
                        data.artifact_group,
44✔
503
                        data.artifact_provides,
44✔
504
                        data.artifact_clears_provides,
44✔
505
                        [](database::Transaction &txn) {
44✔
506
                                return txn.Remove(context::MenderContext::standalone_state_key);
44✔
507
                        });
88✔
508
        }
509
        if (err != error::NoError) {
55✔
UNCOV
510
                err = err.WithContext("Error while updating database");
×
UNCOV
511
                UpdateResult(ctx.result_and_error, {Result::Failed | Result::RollbackFailed, err});
×
UNCOV
512
                poster.PostEvent(StateEvent::Failure);
×
513
                return;
514
        }
515

516
        UpdateResult(ctx.result_and_error, {Result::Cleaned, error::NoError});
55✔
517
        poster.PostEvent(final_event);
55✔
518
}
519

520
void ScriptRunnerState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
347✔
521
        auto err = ctx.script_runner->RunScripts(state_, action_, on_error_);
347✔
522
        if (err != error::NoError) {
347✔
523
                log::Error("Error executing script: " + err.String());
48✔
524
                UpdateResult(ctx.result_and_error, {result_on_error_, err});
24✔
525
                poster.PostEvent(StateEvent::Failure);
24✔
526
                return;
527
        }
528

529
        poster.PostEvent(StateEvent::Success);
323✔
530
}
531

532
void ExitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
87✔
533
        auto err =
534
                ctx.main_context.GetMenderStoreDB().WriteTransaction([&ctx](database::Transaction &txn) {
87✔
535
                        auto exp_bytes = txn.Read(context::MenderContext::standalone_state_key);
87✔
536
                        if (!exp_bytes) {
87✔
537
                                if (exp_bytes.error().code == database::MakeError(database::KeyError, "").code) {
60✔
538
                                        // If the stata data is not saved, just do nothing here.
539
                                        return error::NoError;
60✔
540
                                } else {
UNCOV
541
                                        return exp_bytes.error();
×
542
                                }
543
                        }
544

545
                        // If there is state data, resave it with `failed` set to false. The rationale
546
                        // behind this is that if we have already recorded failure for this run, it will be
547
                        // returned in the error code. That does not mean that we should record error for
548
                        // the next run, which is independent. An example is rollback, if we are somewhere
549
                        // in the rollback flow, we are likely to have a failure here, because the *install*
550
                        // failed. But when we now exit, and then later resume the rollback, the rollback
551
                        // should return success, not failure.
552
                        if (ctx.state_data.failed) {
27✔
553
                                ctx.state_data.failed = false;
1✔
554
                                return SaveStateData(txn, ctx.state_data);
1✔
555
                        } else {
556
                                return error::NoError;
26✔
557
                        }
558
                });
87✔
559
        if (err != error::NoError) {
87✔
UNCOV
560
                UpdateResult(ctx.result_and_error, {Result::Failed, err});
×
UNCOV
561
                poster.PostEvent(StateEvent::Failure);
×
562
                return;
563
        }
564

565
        loop_.Stop();
87✔
566
}
567

568
} // namespace standalone
569
} // namespace update
570
} // 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