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

mendersoftware / mender / 1499142629

16 Oct 2024 06:58PM UTC coverage: 76.305% (-0.06%) from 76.361%
1499142629

push

gitlab-ci

lluiscampos
fix: Invalidate cached inventory

Changelog: Invalidate cached inventory data on unauthentication event
to prevent an issue with which the client would not send inventory
data to the server after being unauthorized and authorized again.

Ticket: MEN-7617

Signed-off-by: Lluis Campos <lluis.campos@northern.tech>

0 of 4 new or added lines in 1 file covered. (0.0%)

268 existing lines in 7 files now uncovered.

7310 of 9580 relevant lines covered (76.3%)

11291.79 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());
×
123
                                return;
×
124
                        }
125

126
                        auto exp_reader = resp->MakeBodyAsyncReader();
1✔
127
                        if (!exp_reader) {
1✔
UNCOV
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(
×
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✔
UNCOV
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()});
×
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✔
UNCOV
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