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

mendersoftware / mender / 969241802

16 Aug 2023 08:29AM UTC coverage: 79.019% (+0.2%) from 78.825%
969241802

push

gitlab-ci

oleorhagen
chore(crypto): Make the PrivateKey constructor public

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

5314 of 6725 relevant lines covered (79.02%)

200.29 hits per line

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

81.4
/mender-update/daemon/states.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/daemon/states.hpp>
16

17
#include <common/conf.hpp>
18
#include <common/events_io.hpp>
19
#include <common/log.hpp>
20

21
#include <mender-update/daemon/context.hpp>
22

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

27
namespace conf = mender::common::conf;
28
namespace error = mender::common::error;
29
namespace events = mender::common::events;
30
namespace log = mender::common::log;
31
namespace kv_db = mender::common::key_value_database;
32

33
namespace main_context = mender::update::context;
34

35
class DefaultStateHandler {
36
public:
37
        void operator()(const error::Error &err) {
142✔
38
                if (err != error::NoError) {
142✔
39
                        log::Error(err.String());
17✔
40
                        poster.PostEvent(StateEvent::Failure);
17✔
41
                        return;
17✔
42
                }
43
                poster.PostEvent(StateEvent::Success);
125✔
44
        }
45

46
        sm::EventPoster<StateEvent> &poster;
47
};
48

49
static void DefaultAsyncErrorHandler(sm::EventPoster<StateEvent> &poster, const error::Error &err) {
201✔
50
        if (err != error::NoError) {
201✔
51
                log::Error(err.String());
×
52
                poster.PostEvent(StateEvent::Failure);
×
53
        }
54
}
201✔
55

56
void EmptyState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
73✔
57
        // Keep this state truly empty.
58
}
73✔
59

60
void IdleState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
59✔
61
        log::Debug("Entering Idle state");
59✔
62
}
59✔
63

64
void SubmitInventoryState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
29✔
65
        log::Debug("Submitting inventory");
29✔
66

67
        // Schedule timer for next update first, so that long running submissions do not postpone
68
        // the schedule.
69
        poll_timer_.AsyncWait(
29✔
70
                chrono::seconds(ctx.mender_context.GetConfig().inventory_poll_interval_seconds),
29✔
71
                [&poster](error::Error err) {
×
72
                        if (err != error::NoError) {
×
73
                                log::Error("Inventory poll timer caused error: " + err.String());
×
74
                        } else {
75
                                poster.PostEvent(StateEvent::InventoryPollingTriggered);
×
76
                        }
77
                });
58✔
78

79
        // TODO: MEN-6576
80
        poster.PostEvent(StateEvent::Success);
29✔
81
}
29✔
82

83
void PollForDeploymentState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
29✔
84
        log::Debug("Polling for update");
29✔
85

86
        // Schedule timer for next update first, so that long running submissions do not postpone
87
        // the schedule.
88
        poll_timer_.AsyncWait(
29✔
89
                chrono::seconds(ctx.mender_context.GetConfig().update_poll_interval_seconds),
29✔
90
                [&poster](error::Error err) {
×
91
                        if (err != error::NoError) {
×
92
                                log::Error("Update poll timer caused error: " + err.String());
×
93
                        } else {
94
                                poster.PostEvent(StateEvent::DeploymentPollingTriggered);
×
95
                        }
96
                });
58✔
97

98
        auto err = ctx.deployment_client->CheckNewDeployments(
29✔
99
                ctx.mender_context,
100
                ctx.mender_context.GetConfig().server_url,
58✔
101
                ctx.http_client,
102
                [&ctx, &poster](mender::update::deployments::CheckUpdatesAPIResponse response) {
140✔
103
                        if (!response) {
28✔
104
                                log::Error("Error while polling for deployment: " + response.error().String());
×
105
                                poster.PostEvent(StateEvent::Failure);
×
106
                                return;
×
107
                        } else if (!response.value()) {
28✔
108
                                log::Info("No update available");
×
109
                                poster.PostEvent(StateEvent::NothingToDo);
×
110
                                return;
×
111
                        }
112

113
                        auto exp_data = ApiResponseJsonToStateData(response.value().value());
28✔
114
                        if (!exp_data) {
28✔
115
                                log::Error("Error in API response: " + exp_data.error().String());
×
116
                                poster.PostEvent(StateEvent::Failure);
×
117
                                return;
×
118
                        }
119

120
                        // Make a new set of update data.
121
                        ctx.deployment.state_data.reset(new StateData(std::move(exp_data.value())));
28✔
122

123
                        log::Info(
28✔
124
                                "Deployment with ID " + ctx.deployment.state_data->update_info.id + " started.");
56✔
125

126
                        poster.PostEvent(StateEvent::DeploymentStarted);
28✔
127
                        poster.PostEvent(StateEvent::Success);
28✔
128
                });
58✔
129

130
        if (err != error::NoError) {
29✔
131
                log::Error("Error when trying to poll for deployment: " + err.String());
1✔
132
                poster.PostEvent(StateEvent::Failure);
1✔
133
        }
134
}
29✔
135

136
void SaveState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
213✔
137
        assert(ctx.deployment.state_data);
213✔
138

139
        ctx.deployment.state_data->state = DatabaseStateString();
213✔
140

141
        auto err = ctx.SaveDeploymentStateData(*ctx.deployment.state_data);
213✔
142
        if (err != error::NoError) {
213✔
143
                log::Error(err.String());
8✔
144
                if (err.code
8✔
145
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
8✔
146
                        poster.PostEvent(StateEvent::StateLoopDetected);
1✔
147
                        return;
1✔
148
                } else if (!IsFailureState()) {
7✔
149
                        // Non-failure states should be interrupted, but failure states should be
150
                        // allowed to do their work, even if a database error was detected.
151
                        poster.PostEvent(StateEvent::Failure);
1✔
152
                        return;
1✔
153
                }
154
        }
155

156
        OnEnterSaveState(ctx, poster);
211✔
157
}
158

159
void UpdateDownloadState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
28✔
160
        log::Debug("Entering Download state");
28✔
161

162
        auto req = make_shared<http::OutgoingRequest>();
28✔
163
        req->SetMethod(http::Method::GET);
28✔
164
        auto err = req->SetAddress(ctx.deployment.state_data->update_info.artifact.source.uri);
28✔
165
        if (err != error::NoError) {
28✔
166
                log::Error(err.String());
×
167
                poster.PostEvent(StateEvent::Failure);
×
168
                return;
×
169
        }
170

171
        err = ctx.download_client.AsyncCall(
28✔
172
                req,
173
                [&ctx, &poster](http::ExpectedIncomingResponsePtr exp_resp) {
111✔
174
                        if (!exp_resp) {
28✔
175
                                log::Error(exp_resp.error().String());
×
176
                                poster.PostEvent(StateEvent::Failure);
×
177
                                return;
1✔
178
                        }
179

180
                        auto &resp = exp_resp.value();
28✔
181
                        if (resp->GetStatusCode() != http::StatusOK) {
28✔
182
                                log::Error(
1✔
183
                                        "Unexpected status code while fetching artifact: " + resp->GetStatusMessage());
2✔
184
                                ctx.download_client.Cancel();
1✔
185
                                poster.PostEvent(StateEvent::Failure);
1✔
186
                                return;
1✔
187
                        }
188

189
                        auto http_reader = resp->MakeBodyAsyncReader();
54✔
190
                        ctx.deployment.artifact_reader =
191
                                make_shared<events::io::ReaderFromAsyncReader>(ctx.event_loop, http_reader);
27✔
192
                        ParseArtifact(ctx, poster);
27✔
193
                },
194
                [](http::ExpectedIncomingResponsePtr exp_resp) {
27✔
195
                        if (!exp_resp) {
27✔
196
                                log::Error(exp_resp.error().String());
×
197
                                // Cannot handle error here, because this handler is called at the
198
                                // end of the download, when we have already left this state. So
199
                                // rely on this error being propagated through the BodyAsyncReader
200
                                // above instead.
201
                                return;
×
202
                        }
203
                });
56✔
204

205
        if (err != error::NoError) {
28✔
206
                log::Error(err.String());
×
207
                poster.PostEvent(StateEvent::Failure);
×
208
                return;
×
209
        }
210
}
211

212
void UpdateDownloadState::ParseArtifact(Context &ctx, sm::EventPoster<StateEvent> &poster) {
27✔
213
        artifact::config::ParserConfig config {
27✔
214
                .artifact_scripts_filesystem_path = conf::paths::DefaultArtScriptsPath,
215
                .artifact_scripts_version = 3,
216
        };
27✔
217
        auto exp_parser = artifact::Parse(*ctx.deployment.artifact_reader, config);
27✔
218
        if (!exp_parser) {
27✔
219
                log::Error(exp_parser.error().String());
×
220
                poster.PostEvent(StateEvent::Failure);
×
221
                return;
×
222
        }
223
        ctx.deployment.artifact_parser.reset(new artifact::Artifact(std::move(exp_parser.value())));
27✔
224

225
        auto exp_header = artifact::View(*ctx.deployment.artifact_parser, 0);
27✔
226
        if (!exp_header) {
27✔
227
                log::Error(exp_header.error().String());
×
228
                poster.PostEvent(StateEvent::Failure);
×
229
                return;
×
230
        }
231
        auto &header = exp_header.value();
27✔
232

233
        auto exp_matches = ctx.mender_context.MatchesArtifactDepends(header.header);
27✔
234
        if (!exp_matches) {
27✔
235
                log::Error(exp_matches.error().String());
×
236
                poster.PostEvent(StateEvent::Failure);
×
237
                return;
×
238
        } else if (!exp_matches.value()) {
27✔
239
                // reasons already logged
240
                poster.PostEvent(StateEvent::Failure);
1✔
241
                return;
1✔
242
        }
243

244
        log::Info("Installing artifact...");
26✔
245

246
        ctx.deployment.state_data->FillUpdateDataFromArtifact(header);
26✔
247

248
        ctx.deployment.state_data->state = Context::kUpdateStateDownload;
26✔
249

250
        assert(ctx.deployment.state_data->update_info.artifact.payload_types.size() == 1);
26✔
251

252
        // Initial state data save, now that we have enough information from the artifact.
253
        auto err = ctx.SaveDeploymentStateData(*ctx.deployment.state_data);
26✔
254
        if (err != error::NoError) {
26✔
255
                log::Error(err.String());
1✔
256
                if (err.code
1✔
257
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
1✔
258
                        poster.PostEvent(StateEvent::StateLoopDetected);
×
259
                        return;
×
260
                } else {
261
                        poster.PostEvent(StateEvent::Failure);
1✔
262
                        return;
1✔
263
                }
264
        }
265

266
        if (header.header.payload_type == "") {
25✔
267
                // Empty-payload-artifact, aka "bootstrap artifact".
268
                poster.PostEvent(StateEvent::NothingToDo);
1✔
269
                return;
1✔
270
        }
271

272
        ctx.deployment.update_module.reset(
24✔
273
                new update_module::UpdateModule(ctx.mender_context, header.header.payload_type));
24✔
274

275
        err = ctx.deployment.update_module->CleanAndPrepareFileTree(
24✔
276
                ctx.deployment.update_module->GetUpdateModuleWorkDir(), header);
48✔
277
        if (err != error::NoError) {
24✔
278
                log::Error(err.String());
×
279
                poster.PostEvent(StateEvent::Failure);
×
280
                return;
×
281
        }
282

283
        auto exp_payload = ctx.deployment.artifact_parser->Next();
24✔
284
        if (!exp_payload) {
24✔
285
                log::Error(exp_payload.error().String());
×
286
                poster.PostEvent(StateEvent::Failure);
×
287
                return;
×
288
        }
289
        ctx.deployment.artifact_payload.reset(new artifact::Payload(std::move(exp_payload.value())));
24✔
290

291
        ctx.deployment.update_module->AsyncDownload(
48✔
292
                ctx.event_loop, *ctx.deployment.artifact_payload, [&poster](error::Error err) {
24✔
293
                        if (err != error::NoError) {
24✔
294
                                log::Error(err.String());
2✔
295
                                poster.PostEvent(StateEvent::Failure);
2✔
296
                                return;
2✔
297
                        }
298

299
                        poster.PostEvent(StateEvent::Success);
22✔
300
                });
48✔
301
}
302

303
SendStatusUpdateState::SendStatusUpdateState(
×
304
        optional::optional<deployments::DeploymentStatus> status) :
135✔
305
        status_(status),
306
        mode_(FailureMode::Ignore) {
×
307
}
×
308

309
SendStatusUpdateState::SendStatusUpdateState(
×
310
        optional::optional<deployments::DeploymentStatus> status,
311
        events::EventLoop &event_loop,
312
        int retry_interval_seconds) :
90✔
313
        status_(status),
314
        mode_(FailureMode::RetryThenFail),
315
        // MEN-2676: Cap at 10 retries.
316
        retry_(
317
                Retry {http::ExponentialBackoff(chrono::seconds(retry_interval_seconds), 10), event_loop}) {
×
318
}
×
319

320
void SendStatusUpdateState::SetSmallestWaitInterval(chrono::milliseconds interval) {
88✔
321
        if (retry_) {
88✔
322
                retry_->backoff.SetSmallestInterval(interval);
88✔
323
        }
324
}
88✔
325

326
void SendStatusUpdateState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
117✔
327
        // Reset this every time we enter the state, which means a new round of retries.
328
        if (retry_) {
117✔
329
                retry_->backoff.Reset();
54✔
330
        }
331

332
        DoStatusUpdate(ctx, poster);
117✔
333
}
117✔
334

335
void SendStatusUpdateState::DoStatusUpdate(Context &ctx, sm::EventPoster<StateEvent> &poster) {
136✔
336
        assert(ctx.deployment_client);
136✔
337
        assert(ctx.deployment.state_data);
136✔
338

339
        log::Info("Sending status update to server");
136✔
340

341
        auto result_handler = [this, &ctx, &poster](const error::Error &err) {
335✔
342
                if (err != error::NoError) {
136✔
343
                        log::Error("Could not send deployment status: " + err.String());
24✔
344

345
                        switch (mode_) {
24✔
346
                        case FailureMode::Ignore:
3✔
347
                                break;
3✔
348
                        case FailureMode::RetryThenFail:
21✔
349
                                if (err.code
21✔
350
                                        == deployments::MakeError(deployments::DeploymentAbortedError, "").code) {
21✔
351
                                        // If the deployment was aborted upstream it is an immediate
352
                                        // failure, even if retry is enabled.
353
                                        poster.PostEvent(StateEvent::Failure);
1✔
354
                                        return;
21✔
355
                                }
356

357
                                auto exp_interval = retry_->backoff.NextInterval();
40✔
358
                                if (!exp_interval) {
20✔
359
                                        log::Error(
1✔
360
                                                "Giving up on sending status updates to server: "
361
                                                + exp_interval.error().String());
2✔
362
                                        poster.PostEvent(StateEvent::Failure);
1✔
363
                                        return;
1✔
364
                                }
365

366
                                log::Info(
19✔
367
                                        "Retrying status update after "
368
                                        + to_string(chrono::milliseconds(*exp_interval).count() / 1000) + " seconds");
38✔
369

370
                                retry_->wait_timer.AsyncWait(
19✔
371
                                        *exp_interval, [this, &ctx, &poster](error::Error err) {
19✔
372
                                                // Error here is quite unexpected (from a timer), so treat
373
                                                // this as an immediate error, despite Retry flag.
374
                                                if (err != error::NoError) {
19✔
375
                                                        log::Error(
×
376
                                                                "Unexpected error in SendStatusUpdateState wait timer: "
377
                                                                + err.String());
×
378
                                                        poster.PostEvent(StateEvent::Failure);
×
379
                                                        return;
×
380
                                                }
381

382
                                                // Try again.
383
                                                DoStatusUpdate(ctx, poster);
19✔
384
                                        });
38✔
385
                                return;
19✔
386
                        }
387
                }
388

389
                poster.PostEvent(StateEvent::Success);
115✔
390
        };
136✔
391

392
        deployments::DeploymentStatus status;
393
        if (status_) {
136✔
394
                status = status_.value();
92✔
395
        } else {
396
                // If nothing is specified, grab success/failure status from the deployment status.
397
                if (ctx.deployment.failed) {
44✔
398
                        status = deployments::DeploymentStatus::Failure;
37✔
399
                } else {
400
                        status = deployments::DeploymentStatus::Success;
7✔
401
                }
402
        }
403

404
        auto err = ctx.deployment_client->PushStatus(
136✔
405
                ctx.deployment.state_data->update_info.id,
136✔
406
                status,
407
                "",
408
                ctx.mender_context.GetConfig().server_url,
272✔
409
                ctx.http_client,
410
                result_handler);
544✔
411

412
        if (err != error::NoError) {
136✔
413
                result_handler(err);
×
414
        }
415

416
        // No action, wait for reply from status endpoint.
417
}
136✔
418

419
void UpdateInstallState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
21✔
420
        log::Debug("Entering ArtifactInstall state");
21✔
421

422
        DefaultAsyncErrorHandler(
21✔
423
                poster,
424
                ctx.deployment.update_module->AsyncArtifactInstall(
21✔
425
                        ctx.event_loop, DefaultStateHandler {poster}));
42✔
426
}
21✔
427

428
void UpdateCheckRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
36✔
429
        DefaultAsyncErrorHandler(
36✔
430
                poster,
431
                ctx.deployment.update_module->AsyncNeedsReboot(
36✔
432
                        ctx.event_loop, [&ctx, &poster](update_module::ExpectedRebootAction reboot_action) {
140✔
433
                                if (!reboot_action.has_value()) {
36✔
434
                                        log::Error(reboot_action.error().String());
2✔
435
                                        poster.PostEvent(StateEvent::Failure);
2✔
436
                                        return;
2✔
437
                                }
438

439
                                ctx.deployment.state_data->update_info.reboot_requested.resize(1);
34✔
440
                                ctx.deployment.state_data->update_info.reboot_requested[0] =
34✔
441
                                        NeedsRebootToDbString(*reboot_action);
68✔
442
                                switch (*reboot_action) {
34✔
443
                                case update_module::RebootAction::No:
4✔
444
                                        poster.PostEvent(StateEvent::NothingToDo);
4✔
445
                                        break;
4✔
446
                                case update_module::RebootAction::Yes:
30✔
447
                                case update_module::RebootAction::Automatic:
448
                                        poster.PostEvent(StateEvent::Success);
30✔
449
                                        break;
30✔
450
                                }
451
                        }));
72✔
452
}
36✔
453

454
void UpdateRebootState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
13✔
455
        log::Debug("Entering ArtifactReboot state");
13✔
456

457
        assert(ctx.deployment.state_data->update_info.reboot_requested.size() == 1);
13✔
458
        auto exp_reboot_mode =
459
                DbStringToNeedsReboot(ctx.deployment.state_data->update_info.reboot_requested[0]);
26✔
460
        // Should always be true because we check it at load time.
461
        assert(exp_reboot_mode);
13✔
462

463
        switch (exp_reboot_mode.value()) {
13✔
464
        case update_module::RebootAction::No:
×
465
                // Should not happen because then we don't enter this state.
466
                assert(false);
×
467
                poster.PostEvent(StateEvent::Failure);
468
                break;
469
        case update_module::RebootAction::Yes:
13✔
470
                DefaultAsyncErrorHandler(
13✔
471
                        poster,
472
                        ctx.deployment.update_module->AsyncArtifactReboot(
13✔
473
                                ctx.event_loop, DefaultStateHandler {poster}));
26✔
474
                break;
13✔
475
        case update_module::RebootAction::Automatic:
×
476
                DefaultAsyncErrorHandler(
×
477
                        poster,
478
                        ctx.deployment.update_module->AsyncSystemReboot(
×
479
                                ctx.event_loop, DefaultStateHandler {poster}));
×
480
                break;
×
481
        }
482
}
13✔
483

484
void UpdateVerifyRebootState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
14✔
485
        log::Debug("Entering ArtifactVerifyReboot state");
14✔
486

487
        DefaultAsyncErrorHandler(
14✔
488
                poster,
489
                ctx.deployment.update_module->AsyncArtifactVerifyReboot(
14✔
490
                        ctx.event_loop, DefaultStateHandler {poster}));
28✔
491
}
14✔
492

493
void UpdateBeforeCommitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
10✔
494
        // It's possible that the update we have done has changed our credentials. Therefore it's
495
        // important that we try to log in from scratch and do not use the token we already have.
496
        ctx.http_client.ExpireToken();
10✔
497

498
        poster.PostEvent(StateEvent::Success);
10✔
499
}
10✔
500

501
void UpdateCommitState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
8✔
502
        log::Debug("Entering ArtifactCommit state");
8✔
503

504
        DefaultAsyncErrorHandler(
8✔
505
                poster,
506
                ctx.deployment.update_module->AsyncArtifactCommit(
8✔
507
                        ctx.event_loop, DefaultStateHandler {poster}));
16✔
508
}
8✔
509

510
void UpdateAfterCommitState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
6✔
511
        // TODO: Will need to run ArtifactCommit_Leave scripts in here. Maybe it should be renamed
512
        // to something with state scripts also.
513

514
        // Now we have committed. If we had a schema update, re-save state data with the new schema.
515
        assert(ctx.deployment.state_data);
6✔
516
        auto &state_data = *ctx.deployment.state_data;
6✔
517
        if (state_data.update_info.has_db_schema_update) {
6✔
518
                state_data.update_info.has_db_schema_update = false;
1✔
519
                auto err = ctx.SaveDeploymentStateData(state_data);
1✔
520
                if (err != error::NoError) {
1✔
521
                        log::Error("Not able to commit schema update: " + err.String());
×
522
                        poster.PostEvent(StateEvent::Failure);
×
523
                        return;
×
524
                }
525
        }
526

527
        poster.PostEvent(StateEvent::Success);
6✔
528
}
529

530
void UpdateCheckRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
23✔
531
        DefaultAsyncErrorHandler(
23✔
532
                poster,
533
                ctx.deployment.update_module->AsyncSupportsRollback(
23✔
534
                        ctx.event_loop, [&ctx, &poster](expected::ExpectedBool rollback_supported) {
88✔
535
                                if (!rollback_supported.has_value()) {
23✔
536
                                        log::Error(rollback_supported.error().String());
1✔
537
                                        poster.PostEvent(StateEvent::Failure);
1✔
538
                                        return;
1✔
539
                                }
540

541
                                ctx.deployment.state_data->update_info.supports_rollback =
22✔
542
                                        SupportsRollbackToDbString(*rollback_supported);
44✔
543
                                if (*rollback_supported) {
22✔
544
                                        poster.PostEvent(StateEvent::RollbackStarted);
20✔
545
                                        poster.PostEvent(StateEvent::Success);
20✔
546
                                } else {
547
                                        poster.PostEvent(StateEvent::NothingToDo);
2✔
548
                                }
549
                        }));
46✔
550
}
23✔
551

552
void UpdateRollbackState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
21✔
553
        log::Debug("Entering ArtifactRollback state");
21✔
554

555
        DefaultAsyncErrorHandler(
21✔
556
                poster,
557
                ctx.deployment.update_module->AsyncArtifactRollback(
21✔
558
                        ctx.event_loop, DefaultStateHandler {poster}));
42✔
559
}
21✔
560

561
void UpdateRollbackRebootState::OnEnterSaveState(
29✔
562
        Context &ctx, sm::EventPoster<StateEvent> &poster) {
563
        log::Debug("Entering ArtifactRollbackReboot state");
29✔
564

565
        // We ignore errors in this state as long as the ArtifactVerifyRollbackReboot state
566
        // succeeds.
567
        auto err = ctx.deployment.update_module->AsyncArtifactRollbackReboot(
568
                ctx.event_loop, [&poster](error::Error err) {
58✔
569
                        if (err != error::NoError) {
29✔
570
                                log::Error(err.String());
1✔
571
                        }
572
                        poster.PostEvent(StateEvent::Success);
29✔
573
                });
87✔
574

575
        if (err != error::NoError) {
29✔
576
                log::Error(err.String());
×
577
                poster.PostEvent(StateEvent::Success);
×
578
        }
579
}
29✔
580

581
void UpdateVerifyRollbackRebootState::OnEnterSaveState(
30✔
582
        Context &ctx, sm::EventPoster<StateEvent> &poster) {
583
        log::Debug("Entering ArtifactVerifyRollbackReboot state");
30✔
584

585
        // In this state we only retry, we don't fail. If this keeps on going forever, then the
586
        // state loop detection will eventually kick in.
587
        auto err = ctx.deployment.update_module->AsyncArtifactVerifyRollbackReboot(
588
                ctx.event_loop, [&poster](error::Error err) {
60✔
589
                        if (err != error::NoError) {
30✔
590
                                log::Error(err.String());
12✔
591
                                poster.PostEvent(StateEvent::Retry);
12✔
592
                                return;
12✔
593
                        }
594
                        poster.PostEvent(StateEvent::Success);
18✔
595
                });
60✔
596
        if (err != error::NoError) {
30✔
597
                log::Error(err.String());
×
598
                poster.PostEvent(StateEvent::Retry);
×
599
        }
600
}
30✔
601

602
void UpdateRollbackSuccessfulState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
25✔
603
        ctx.deployment.state_data->update_info.all_rollbacks_successful = true;
25✔
604
        poster.PostEvent(StateEvent::Success);
25✔
605
}
25✔
606

607
void UpdateFailureState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
27✔
608
        log::Debug("Entering ArtifactFailure state");
27✔
609

610
        DefaultAsyncErrorHandler(
27✔
611
                poster,
612
                ctx.deployment.update_module->AsyncArtifactFailure(
27✔
613
                        ctx.event_loop, DefaultStateHandler {poster}));
54✔
614
}
27✔
615

616
static string AddInconsistentSuffix(const string &str) {
8✔
617
        const auto &suffix = main_context::MenderContext::broken_artifact_name_suffix;
8✔
618
        // `string::ends_with` is C++20... grumble
619
        string ret {str};
8✔
620
        if (!common::EndsWith(ret, suffix)) {
8✔
621
                ret.append(suffix);
8✔
622
        }
623
        return ret;
8✔
624
}
625

626
void UpdateSaveProvidesState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
34✔
627
        if (ctx.deployment.failed && !ctx.deployment.rollback_failed) {
34✔
628
                // If the update failed, but we rolled back successfully, then we don't need to do
629
                // anything, just keep the old data.
630
                poster.PostEvent(StateEvent::Success);
21✔
631
                return;
21✔
632
        }
633

634
        assert(ctx.deployment.state_data);
13✔
635
        // This state should never happen: rollback failed, but update not failed??
636
        assert(!(!ctx.deployment.failed && ctx.deployment.rollback_failed));
13✔
637

638
        // We expect Cleanup to be the next state after this.
639
        ctx.deployment.state_data->state = ctx.kUpdateStateCleanup;
13✔
640

641
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
13✔
642

643
        string artifact_name;
13✔
644
        if (ctx.deployment.rollback_failed) {
13✔
645
                artifact_name = AddInconsistentSuffix(artifact.artifact_name);
6✔
646
        } else {
647
                artifact_name = artifact.artifact_name;
7✔
648
        }
649

650
        auto err = ctx.mender_context.CommitArtifactData(
13✔
651
                artifact_name,
652
                artifact.artifact_group,
13✔
653
                artifact.type_info_provides,
13✔
654
                artifact.clears_artifact_provides,
13✔
655
                [&ctx](kv_db::Transaction &txn) {
13✔
656
                        // Save the Cleanup state together with the artifact data, atomically.
657
                        return ctx.SaveDeploymentStateData(txn, *ctx.deployment.state_data);
13✔
658
                });
26✔
659
        if (err != error::NoError) {
13✔
660
                log::Error("Error saving artifact data: " + err.String());
×
661
                if (err.code
×
662
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
×
663
                        poster.PostEvent(StateEvent::StateLoopDetected);
×
664
                        return;
×
665
                }
666
                poster.PostEvent(StateEvent::Failure);
×
667
                return;
×
668
        }
669

670
        poster.PostEvent(StateEvent::Success);
13✔
671
}
672

673
void UpdateCleanupState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
42✔
674
        log::Debug("Entering ArtifactCleanup state");
42✔
675

676
        // It's possible for there not to be an initialized update_module structure, if the
677
        // deployment failed before we could successfully parse the artifact. If so, cleanup is a
678
        // no-op.
679
        if (!ctx.deployment.update_module) {
42✔
680
                poster.PostEvent(StateEvent::Success);
4✔
681
                return;
4✔
682
        }
683

684
        DefaultAsyncErrorHandler(
38✔
685
                poster,
686
                ctx.deployment.update_module->AsyncCleanup(ctx.event_loop, DefaultStateHandler {poster}));
76✔
687
}
688

689
void ClearArtifactDataState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
44✔
690
        auto err = ctx.mender_context.GetMenderStoreDB().WriteTransaction([](kv_db::Transaction &txn) {
44✔
691
                // Remove state data, since we're done now.
692
                auto err = txn.Remove(main_context::MenderContext::state_data_key);
84✔
693
                if (err != error::NoError) {
42✔
694
                        return err;
×
695
                }
696
                return txn.Remove(main_context::MenderContext::state_data_key_uncommitted);
42✔
697
        });
44✔
698
        if (err != error::NoError) {
44✔
699
                log::Error("Error removing artifact data: " + err.String());
2✔
700
                poster.PostEvent(StateEvent::Failure);
2✔
701
                return;
2✔
702
        }
703

704
        poster.PostEvent(StateEvent::Success);
42✔
705
}
706

707
void StateLoopState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
2✔
708
        assert(ctx.deployment.state_data);
2✔
709
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
2✔
710

711
        // Mark update as inconsistent.
712
        string artifact_name = AddInconsistentSuffix(artifact.artifact_name);
2✔
713

714
        auto err = ctx.mender_context.CommitArtifactData(
2✔
715
                artifact_name,
716
                artifact.artifact_group,
2✔
717
                artifact.type_info_provides,
2✔
718
                artifact.clears_artifact_provides,
2✔
719
                [](kv_db::Transaction &txn) { return error::NoError; });
6✔
720
        if (err != error::NoError) {
2✔
721
                log::Error("Error saving inconsistent artifact data: " + err.String());
×
722
                poster.PostEvent(StateEvent::Failure);
×
723
                return;
×
724
        }
725

726
        poster.PostEvent(StateEvent::Success);
2✔
727
}
728

729
void EndOfDeploymentState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
44✔
730
        ctx.deployment = {};
44✔
731
        poster.PostEvent(StateEvent::DeploymentEnded);
44✔
732
        poster.PostEvent(StateEvent::Success);
44✔
733
}
44✔
734

735
ExitState::ExitState(events::EventLoop &event_loop) :
×
736
        event_loop_(event_loop) {
×
737
}
×
738

739
void ExitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
44✔
740
        event_loop_.Stop();
44✔
741
}
44✔
742

743
namespace deployment_tracking {
744

745
void NoFailuresState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
30✔
746
        ctx.deployment.failed = false;
30✔
747
        ctx.deployment.rollback_failed = false;
30✔
748
}
30✔
749

750
void FailureState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
30✔
751
        ctx.deployment.failed = true;
30✔
752
        ctx.deployment.rollback_failed = true;
30✔
753
}
30✔
754

755
void RollbackAttemptedState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
26✔
756
        ctx.deployment.failed = true;
26✔
757
        ctx.deployment.rollback_failed = false;
26✔
758
}
26✔
759

760
void RollbackFailedState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
6✔
761
        ctx.deployment.failed = true;
6✔
762
        ctx.deployment.rollback_failed = true;
6✔
763
}
6✔
764

765
} // namespace deployment_tracking
766

767
} // namespace daemon
768
} // namespace update
769
} // 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