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

mendersoftware / mender / 974575668

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

push

gitlab-ci

kacf
chore: Implement pushing of logs to the server.

Ticket: MEN-6581

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

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

5492 of 6967 relevant lines covered (78.83%)

238.75 hits per line

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

82.42
/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) {
77✔
57
        // Keep this state truly empty.
58
}
77✔
59

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

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

67
        // Schedule timer for next update first, so that long running submissions do not postpone
68
        // the schedule.
69
        poll_timer_.AsyncWait(
31✔
70
                chrono::seconds(ctx.mender_context.GetConfig().inventory_poll_interval_seconds),
31✔
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
                });
62✔
78

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

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

86
        // Schedule timer for next update first, so that long running submissions do not postpone
87
        // the schedule.
88
        poll_timer_.AsyncWait(
31✔
89
                chrono::seconds(ctx.mender_context.GetConfig().update_poll_interval_seconds),
31✔
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
                });
62✔
97

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

113
                        auto exp_data = ApiResponseJsonToStateData(response.value().value());
30✔
114
                        if (!exp_data) {
30✔
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())));
30✔
122

123
                        ctx.BeginDeploymentLogging();
30✔
124

125
                        log::Info("Running Mender client " + conf::kMenderVersion);
30✔
126
                        log::Info(
30✔
127
                                "Deployment with ID " + ctx.deployment.state_data->update_info.id + " started.");
60✔
128

129
                        poster.PostEvent(StateEvent::DeploymentStarted);
30✔
130
                        poster.PostEvent(StateEvent::Success);
30✔
131
                });
62✔
132

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

139
void SaveState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
215✔
140
        assert(ctx.deployment.state_data);
215✔
141

142
        ctx.deployment.state_data->state = DatabaseStateString();
215✔
143

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

159
        OnEnterSaveState(ctx, poster);
213✔
160
}
161

162
void UpdateDownloadState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
30✔
163
        log::Debug("Entering Download state");
30✔
164

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

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

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

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

208
        if (err != error::NoError) {
30✔
209
                log::Error(err.String());
×
210
                poster.PostEvent(StateEvent::Failure);
×
211
                return;
×
212
        }
213
}
214

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

229
        auto exp_header = artifact::View(*ctx.deployment.artifact_parser, 0);
29✔
230
        if (!exp_header) {
29✔
231
                log::Error(exp_header.error().String());
×
232
                poster.PostEvent(StateEvent::Failure);
×
233
                return;
×
234
        }
235
        auto &header = exp_header.value();
29✔
236

237
        auto exp_matches = ctx.mender_context.MatchesArtifactDepends(header.header);
29✔
238
        if (!exp_matches) {
29✔
239
                log::Error(exp_matches.error().String());
2✔
240
                poster.PostEvent(StateEvent::Failure);
2✔
241
                return;
2✔
242
        } else if (!exp_matches.value()) {
27✔
243
                // reasons already logged
244
                poster.PostEvent(StateEvent::Failure);
1✔
245
                return;
1✔
246
        }
247

248
        log::Info("Installing artifact...");
26✔
249

250
        ctx.deployment.state_data->FillUpdateDataFromArtifact(header);
26✔
251

252
        ctx.deployment.state_data->state = Context::kUpdateStateDownload;
26✔
253

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

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

270
        if (header.header.payload_type == "") {
25✔
271
                // Empty-payload-artifact, aka "bootstrap artifact".
272
                poster.PostEvent(StateEvent::NothingToDo);
1✔
273
                return;
1✔
274
        }
275

276
        ctx.deployment.update_module.reset(
24✔
277
                new update_module::UpdateModule(ctx.mender_context, header.header.payload_type));
24✔
278

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

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

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

303
                        poster.PostEvent(StateEvent::Success);
22✔
304
                });
48✔
305
}
306

307
SendStatusUpdateState::SendStatusUpdateState(
×
308
        optional::optional<deployments::DeploymentStatus> status) :
141✔
309
        status_(status),
310
        mode_(FailureMode::Ignore) {
×
311
}
×
312

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

324
void SendStatusUpdateState::SetSmallestWaitInterval(chrono::milliseconds interval) {
88✔
325
        if (retry_) {
88✔
326
                retry_->backoff.SetSmallestInterval(interval);
88✔
327
        }
328
}
88✔
329

330
void SendStatusUpdateState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
121✔
331
        // Reset this every time we enter the state, which means a new round of retries.
332
        if (retry_) {
121✔
333
                retry_->backoff.Reset();
56✔
334
        }
335

336
        DoStatusUpdate(ctx, poster);
121✔
337
}
121✔
338

339
void SendStatusUpdateState::DoStatusUpdate(Context &ctx, sm::EventPoster<StateEvent> &poster) {
140✔
340
        assert(ctx.deployment_client);
140✔
341
        assert(ctx.deployment.state_data);
140✔
342

343
        log::Info("Sending status update to server");
140✔
344

345
        auto result_handler = [this, &ctx, &poster](const error::Error &err) {
343✔
346
                if (err != error::NoError) {
140✔
347
                        log::Error("Could not send deployment status: " + err.String());
24✔
348

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

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

370
                                log::Info(
19✔
371
                                        "Retrying status update after "
372
                                        + to_string(chrono::milliseconds(*exp_interval).count() / 1000) + " seconds");
38✔
373

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

386
                                                // Try again. Since both status and logs are sent
387
                                                // from here, there's a chance this might resubmit
388
                                                // the status, but there's no harm in it, and it
389
                                                // won't happen often.
390
                                                DoStatusUpdate(ctx, poster);
19✔
391
                                        });
38✔
392
                                return;
19✔
393
                        }
394
                }
395

396
                poster.PostEvent(StateEvent::Success);
119✔
397
        };
140✔
398

399
        deployments::DeploymentStatus status;
400
        if (status_) {
140✔
401
                status = status_.value();
94✔
402
        } else {
403
                // If nothing is specified, grab success/failure status from the deployment status.
404
                if (ctx.deployment.failed) {
46✔
405
                        status = deployments::DeploymentStatus::Failure;
39✔
406
                } else {
407
                        status = deployments::DeploymentStatus::Success;
7✔
408
                }
409
        }
410

411
        // Push status.
412
        auto err = ctx.deployment_client->PushStatus(
140✔
413
                ctx.deployment.state_data->update_info.id,
140✔
414
                status,
415
                "",
416
                ctx.mender_context.GetConfig().server_url,
280✔
417
                ctx.http_client,
418
                [result_handler, &ctx](error::Error err) {
412✔
419
                        // If there is an error, we don't submit logs now, but call the handler,
420
                        // which may schedule a retry later. If there is no error, and the
421
                        // deployment as a whole was successful, then also call the handler here,
422
                        // since we don't need to submit logs at all then.
423
                        if (err != error::NoError || !ctx.deployment.failed) {
140✔
424
                                result_handler(err);
101✔
425
                                return;
101✔
426
                        }
427

428
                        // Push logs.
429
                        err = ctx.deployment_client->PushLogs(
78✔
430
                                ctx.deployment.state_data->update_info.id,
39✔
431
                                ctx.deployment.logger->LogFilePath(),
78✔
432
                                ctx.mender_context.GetConfig().server_url,
39✔
433
                                ctx.http_client,
434
                                result_handler);
117✔
435

436
                        if (err != error::NoError) {
39✔
437
                                result_handler(err);
×
438
                        }
439
                });
560✔
440

441
        if (err != error::NoError) {
140✔
442
                result_handler(err);
×
443
        }
444

445
        // No action, wait for reply from status endpoint.
446
}
140✔
447

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

451
        DefaultAsyncErrorHandler(
21✔
452
                poster,
453
                ctx.deployment.update_module->AsyncArtifactInstall(
21✔
454
                        ctx.event_loop, DefaultStateHandler {poster}));
42✔
455
}
21✔
456

457
void UpdateCheckRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
36✔
458
        DefaultAsyncErrorHandler(
36✔
459
                poster,
460
                ctx.deployment.update_module->AsyncNeedsReboot(
36✔
461
                        ctx.event_loop, [&ctx, &poster](update_module::ExpectedRebootAction reboot_action) {
140✔
462
                                if (!reboot_action.has_value()) {
36✔
463
                                        log::Error(reboot_action.error().String());
2✔
464
                                        poster.PostEvent(StateEvent::Failure);
2✔
465
                                        return;
2✔
466
                                }
467

468
                                ctx.deployment.state_data->update_info.reboot_requested.resize(1);
34✔
469
                                ctx.deployment.state_data->update_info.reboot_requested[0] =
34✔
470
                                        NeedsRebootToDbString(*reboot_action);
68✔
471
                                switch (*reboot_action) {
34✔
472
                                case update_module::RebootAction::No:
4✔
473
                                        poster.PostEvent(StateEvent::NothingToDo);
4✔
474
                                        break;
4✔
475
                                case update_module::RebootAction::Yes:
30✔
476
                                case update_module::RebootAction::Automatic:
477
                                        poster.PostEvent(StateEvent::Success);
30✔
478
                                        break;
30✔
479
                                }
480
                        }));
72✔
481
}
36✔
482

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

486
        assert(ctx.deployment.state_data->update_info.reboot_requested.size() == 1);
13✔
487
        auto exp_reboot_mode =
488
                DbStringToNeedsReboot(ctx.deployment.state_data->update_info.reboot_requested[0]);
26✔
489
        // Should always be true because we check it at load time.
490
        assert(exp_reboot_mode);
13✔
491

492
        switch (exp_reboot_mode.value()) {
13✔
493
        case update_module::RebootAction::No:
×
494
                // Should not happen because then we don't enter this state.
495
                assert(false);
×
496
                poster.PostEvent(StateEvent::Failure);
497
                break;
498
        case update_module::RebootAction::Yes:
13✔
499
                DefaultAsyncErrorHandler(
13✔
500
                        poster,
501
                        ctx.deployment.update_module->AsyncArtifactReboot(
13✔
502
                                ctx.event_loop, DefaultStateHandler {poster}));
26✔
503
                break;
13✔
504
        case update_module::RebootAction::Automatic:
×
505
                DefaultAsyncErrorHandler(
×
506
                        poster,
507
                        ctx.deployment.update_module->AsyncSystemReboot(
×
508
                                ctx.event_loop, DefaultStateHandler {poster}));
×
509
                break;
×
510
        }
511
}
13✔
512

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

516
        DefaultAsyncErrorHandler(
14✔
517
                poster,
518
                ctx.deployment.update_module->AsyncArtifactVerifyReboot(
14✔
519
                        ctx.event_loop, DefaultStateHandler {poster}));
28✔
520
}
14✔
521

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

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

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

533
        DefaultAsyncErrorHandler(
8✔
534
                poster,
535
                ctx.deployment.update_module->AsyncArtifactCommit(
8✔
536
                        ctx.event_loop, DefaultStateHandler {poster}));
16✔
537
}
8✔
538

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

543
        // Now we have committed. If we had a schema update, re-save state data with the new schema.
544
        assert(ctx.deployment.state_data);
6✔
545
        auto &state_data = *ctx.deployment.state_data;
6✔
546
        if (state_data.update_info.has_db_schema_update) {
6✔
547
                state_data.update_info.has_db_schema_update = false;
1✔
548
                auto err = ctx.SaveDeploymentStateData(state_data);
1✔
549
                if (err != error::NoError) {
1✔
550
                        log::Error("Not able to commit schema update: " + err.String());
×
551
                        poster.PostEvent(StateEvent::Failure);
×
552
                        return;
×
553
                }
554
        }
555

556
        poster.PostEvent(StateEvent::Success);
6✔
557
}
558

559
void UpdateCheckRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
23✔
560
        DefaultAsyncErrorHandler(
23✔
561
                poster,
562
                ctx.deployment.update_module->AsyncSupportsRollback(
23✔
563
                        ctx.event_loop, [&ctx, &poster](expected::ExpectedBool rollback_supported) {
88✔
564
                                if (!rollback_supported.has_value()) {
23✔
565
                                        log::Error(rollback_supported.error().String());
1✔
566
                                        poster.PostEvent(StateEvent::Failure);
1✔
567
                                        return;
1✔
568
                                }
569

570
                                ctx.deployment.state_data->update_info.supports_rollback =
22✔
571
                                        SupportsRollbackToDbString(*rollback_supported);
44✔
572
                                if (*rollback_supported) {
22✔
573
                                        poster.PostEvent(StateEvent::RollbackStarted);
20✔
574
                                        poster.PostEvent(StateEvent::Success);
20✔
575
                                } else {
576
                                        poster.PostEvent(StateEvent::NothingToDo);
2✔
577
                                }
578
                        }));
46✔
579
}
23✔
580

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

584
        DefaultAsyncErrorHandler(
21✔
585
                poster,
586
                ctx.deployment.update_module->AsyncArtifactRollback(
21✔
587
                        ctx.event_loop, DefaultStateHandler {poster}));
42✔
588
}
21✔
589

590
void UpdateRollbackRebootState::OnEnterSaveState(
29✔
591
        Context &ctx, sm::EventPoster<StateEvent> &poster) {
592
        log::Debug("Entering ArtifactRollbackReboot state");
29✔
593

594
        // We ignore errors in this state as long as the ArtifactVerifyRollbackReboot state
595
        // succeeds.
596
        auto err = ctx.deployment.update_module->AsyncArtifactRollbackReboot(
597
                ctx.event_loop, [&poster](error::Error err) {
58✔
598
                        if (err != error::NoError) {
29✔
599
                                log::Error(err.String());
1✔
600
                        }
601
                        poster.PostEvent(StateEvent::Success);
29✔
602
                });
87✔
603

604
        if (err != error::NoError) {
29✔
605
                log::Error(err.String());
×
606
                poster.PostEvent(StateEvent::Success);
×
607
        }
608
}
29✔
609

610
void UpdateVerifyRollbackRebootState::OnEnterSaveState(
30✔
611
        Context &ctx, sm::EventPoster<StateEvent> &poster) {
612
        log::Debug("Entering ArtifactVerifyRollbackReboot state");
30✔
613

614
        // In this state we only retry, we don't fail. If this keeps on going forever, then the
615
        // state loop detection will eventually kick in.
616
        auto err = ctx.deployment.update_module->AsyncArtifactVerifyRollbackReboot(
617
                ctx.event_loop, [&poster](error::Error err) {
60✔
618
                        if (err != error::NoError) {
30✔
619
                                log::Error(err.String());
12✔
620
                                poster.PostEvent(StateEvent::Retry);
12✔
621
                                return;
12✔
622
                        }
623
                        poster.PostEvent(StateEvent::Success);
18✔
624
                });
60✔
625
        if (err != error::NoError) {
30✔
626
                log::Error(err.String());
×
627
                poster.PostEvent(StateEvent::Retry);
×
628
        }
629
}
30✔
630

631
void UpdateRollbackSuccessfulState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
27✔
632
        ctx.deployment.state_data->update_info.all_rollbacks_successful = true;
27✔
633
        poster.PostEvent(StateEvent::Success);
27✔
634
}
27✔
635

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

639
        DefaultAsyncErrorHandler(
27✔
640
                poster,
641
                ctx.deployment.update_module->AsyncArtifactFailure(
27✔
642
                        ctx.event_loop, DefaultStateHandler {poster}));
54✔
643
}
27✔
644

645
static string AddInconsistentSuffix(const string &str) {
8✔
646
        const auto &suffix = main_context::MenderContext::broken_artifact_name_suffix;
8✔
647
        // `string::ends_with` is C++20... grumble
648
        string ret {str};
8✔
649
        if (!common::EndsWith(ret, suffix)) {
8✔
650
                ret.append(suffix);
8✔
651
        }
652
        return ret;
8✔
653
}
654

655
void UpdateSaveProvidesState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
34✔
656
        if (ctx.deployment.failed && !ctx.deployment.rollback_failed) {
34✔
657
                // If the update failed, but we rolled back successfully, then we don't need to do
658
                // anything, just keep the old data.
659
                poster.PostEvent(StateEvent::Success);
21✔
660
                return;
21✔
661
        }
662

663
        assert(ctx.deployment.state_data);
13✔
664
        // This state should never happen: rollback failed, but update not failed??
665
        assert(!(!ctx.deployment.failed && ctx.deployment.rollback_failed));
13✔
666

667
        // We expect Cleanup to be the next state after this.
668
        ctx.deployment.state_data->state = ctx.kUpdateStateCleanup;
13✔
669

670
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
13✔
671

672
        string artifact_name;
13✔
673
        if (ctx.deployment.rollback_failed) {
13✔
674
                artifact_name = AddInconsistentSuffix(artifact.artifact_name);
6✔
675
        } else {
676
                artifact_name = artifact.artifact_name;
7✔
677
        }
678

679
        auto err = ctx.mender_context.CommitArtifactData(
13✔
680
                artifact_name,
681
                artifact.artifact_group,
13✔
682
                artifact.type_info_provides,
13✔
683
                artifact.clears_artifact_provides,
13✔
684
                [&ctx](kv_db::Transaction &txn) {
13✔
685
                        // Save the Cleanup state together with the artifact data, atomically.
686
                        return ctx.SaveDeploymentStateData(txn, *ctx.deployment.state_data);
13✔
687
                });
26✔
688
        if (err != error::NoError) {
13✔
689
                log::Error("Error saving artifact data: " + err.String());
×
690
                if (err.code
×
691
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
×
692
                        poster.PostEvent(StateEvent::StateLoopDetected);
×
693
                        return;
×
694
                }
695
                poster.PostEvent(StateEvent::Failure);
×
696
                return;
×
697
        }
698

699
        poster.PostEvent(StateEvent::Success);
13✔
700
}
701

702
void UpdateCleanupState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
44✔
703
        log::Debug("Entering ArtifactCleanup state");
44✔
704

705
        // It's possible for there not to be an initialized update_module structure, if the
706
        // deployment failed before we could successfully parse the artifact. If so, cleanup is a
707
        // no-op.
708
        if (!ctx.deployment.update_module) {
44✔
709
                poster.PostEvent(StateEvent::Success);
6✔
710
                return;
6✔
711
        }
712

713
        DefaultAsyncErrorHandler(
38✔
714
                poster,
715
                ctx.deployment.update_module->AsyncCleanup(ctx.event_loop, DefaultStateHandler {poster}));
76✔
716
}
717

718
void ClearArtifactDataState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
46✔
719
        auto err = ctx.mender_context.GetMenderStoreDB().WriteTransaction([](kv_db::Transaction &txn) {
46✔
720
                // Remove state data, since we're done now.
721
                auto err = txn.Remove(main_context::MenderContext::state_data_key);
88✔
722
                if (err != error::NoError) {
44✔
723
                        return err;
×
724
                }
725
                return txn.Remove(main_context::MenderContext::state_data_key_uncommitted);
44✔
726
        });
46✔
727
        if (err != error::NoError) {
46✔
728
                log::Error("Error removing artifact data: " + err.String());
2✔
729
                poster.PostEvent(StateEvent::Failure);
2✔
730
                return;
2✔
731
        }
732

733
        poster.PostEvent(StateEvent::Success);
44✔
734
}
735

736
void StateLoopState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
2✔
737
        assert(ctx.deployment.state_data);
2✔
738
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
2✔
739

740
        // Mark update as inconsistent.
741
        string artifact_name = AddInconsistentSuffix(artifact.artifact_name);
2✔
742

743
        auto err = ctx.mender_context.CommitArtifactData(
2✔
744
                artifact_name,
745
                artifact.artifact_group,
2✔
746
                artifact.type_info_provides,
2✔
747
                artifact.clears_artifact_provides,
2✔
748
                [](kv_db::Transaction &txn) { return error::NoError; });
6✔
749
        if (err != error::NoError) {
2✔
750
                log::Error("Error saving inconsistent artifact data: " + err.String());
×
751
                poster.PostEvent(StateEvent::Failure);
×
752
                return;
×
753
        }
754

755
        poster.PostEvent(StateEvent::Success);
2✔
756
}
757

758
void EndOfDeploymentState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
46✔
759
        ctx.FinishDeploymentLogging();
46✔
760

761
        ctx.deployment = {};
46✔
762
        poster.PostEvent(StateEvent::DeploymentEnded);
46✔
763
        poster.PostEvent(StateEvent::Success);
46✔
764
}
46✔
765

766
ExitState::ExitState(events::EventLoop &event_loop) :
×
767
        event_loop_(event_loop) {
×
768
}
×
769

770
void ExitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
46✔
771
        event_loop_.Stop();
46✔
772
}
46✔
773

774
namespace deployment_tracking {
775

776
void NoFailuresState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
32✔
777
        ctx.deployment.failed = false;
32✔
778
        ctx.deployment.rollback_failed = false;
32✔
779
}
32✔
780

781
void FailureState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
32✔
782
        ctx.deployment.failed = true;
32✔
783
        ctx.deployment.rollback_failed = true;
32✔
784
}
32✔
785

786
void RollbackAttemptedState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
26✔
787
        ctx.deployment.failed = true;
26✔
788
        ctx.deployment.rollback_failed = false;
26✔
789
}
26✔
790

791
void RollbackFailedState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
6✔
792
        ctx.deployment.failed = true;
6✔
793
        ctx.deployment.rollback_failed = true;
6✔
794
}
6✔
795

796
} // namespace deployment_tracking
797

798
} // namespace daemon
799
} // namespace update
800
} // 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