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

mendersoftware / mender / 992981781

05 Sep 2023 08:55AM UTC coverage: 79.541% (+0.3%) from 79.264%
992981781

push

gitlab-ci

lluiscampos
chore: Clean-up `gmock` use in tests

Many tests had the header and include path while only using GTest.

Completely unnecessary clean-up...

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

5719 of 7190 relevant lines covered (79.54%)

290.46 hits per line

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

83.51
/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
#include <common/path.hpp>
21

22
#include <mender-update/daemon/context.hpp>
23
#include <mender-update/inventory.hpp>
24

25
namespace mender {
26
namespace update {
27
namespace daemon {
28

29
namespace conf = mender::common::conf;
30
namespace error = mender::common::error;
31
namespace events = mender::common::events;
32
namespace log = mender::common::log;
33
namespace kv_db = mender::common::key_value_database;
34
namespace path = mender::common::path;
35

36
namespace main_context = mender::update::context;
37
namespace inventory = mender::update::inventory;
38

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

50
        sm::EventPoster<StateEvent> &poster;
51
};
52

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

60
void EmptyState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
78✔
61
        // Keep this state truly empty.
62
}
78✔
63

64
void IdleState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
66✔
65
        log::Debug("Entering Idle state");
66✔
66
}
66✔
67

68
void SubmitInventoryState::DoSubmitInventory(Context &ctx, sm::EventPoster<StateEvent> &poster) {
33✔
69
        log::Debug("Submitting inventory");
33✔
70

71
        auto handler = [&poster](error::Error err) {
66✔
72
                if (err != error::NoError) {
33✔
73
                        log::Error("Failed to submit inventory: " + err.String());
1✔
74
                }
75
                poster.PostEvent((err == error::NoError) ? StateEvent::Success : StateEvent::Failure);
33✔
76
        };
33✔
77

78
        auto err = ctx.inventory_client->PushData(
33✔
79
                ctx.mender_context.GetConfig().paths.GetInventoryScriptsDir(),
66✔
80
                ctx.mender_context.GetConfig().server_url,
33✔
81
                ctx.event_loop,
82
                ctx.http_client,
83
                handler);
99✔
84

85
        if (err != error::NoError) {
33✔
86
                // This is the only case the handler won't be called for us by
87
                // PushData() (see inventory::PushInventoryData()).
88
                handler(err);
1✔
89
        }
90
}
33✔
91

92
void SubmitInventoryState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
33✔
93
        // Schedule timer for next update first, so that long running submissions do not postpone
94
        // the schedule.
95
        poll_timer_.AsyncWait(
33✔
96
                chrono::seconds(ctx.mender_context.GetConfig().inventory_poll_interval_seconds),
33✔
97
                [&poster](error::Error err) {
2✔
98
                        if (err != error::NoError) {
1✔
99
                                log::Error("Inventory poll timer caused error: " + err.String());
×
100
                        } else {
101
                                poster.PostEvent(StateEvent::InventoryPollingTriggered);
1✔
102
                        }
103
                });
67✔
104

105
        DoSubmitInventory(ctx, poster);
33✔
106
}
33✔
107

108
void PollForDeploymentState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
32✔
109
        log::Debug("Polling for update");
32✔
110

111
        // Schedule timer for next update first, so that long running submissions do not postpone
112
        // the schedule.
113
        poll_timer_.AsyncWait(
32✔
114
                chrono::seconds(ctx.mender_context.GetConfig().update_poll_interval_seconds),
32✔
115
                [&poster](error::Error err) {
×
116
                        if (err != error::NoError) {
×
117
                                log::Error("Update poll timer caused error: " + err.String());
×
118
                        } else {
119
                                poster.PostEvent(StateEvent::DeploymentPollingTriggered);
×
120
                        }
121
                });
64✔
122

123
        auto err = ctx.deployment_client->CheckNewDeployments(
32✔
124
                ctx.mender_context,
125
                ctx.mender_context.GetConfig().server_url,
64✔
126
                ctx.http_client,
127
                [&ctx, &poster](mender::update::deployments::CheckUpdatesAPIResponse response) {
180✔
128
                        if (!response) {
30✔
129
                                log::Error("Error while polling for deployment: " + response.error().String());
×
130
                                poster.PostEvent(StateEvent::Failure);
×
131
                                return;
×
132
                        } else if (!response.value()) {
30✔
133
                                log::Info("No update available");
×
134
                                poster.PostEvent(StateEvent::NothingToDo);
×
135
                                return;
×
136
                        }
137

138
                        auto exp_data = ApiResponseJsonToStateData(response.value().value());
30✔
139
                        if (!exp_data) {
30✔
140
                                log::Error("Error in API response: " + exp_data.error().String());
×
141
                                poster.PostEvent(StateEvent::Failure);
×
142
                                return;
×
143
                        }
144

145
                        // Make a new set of update data.
146
                        ctx.deployment.state_data.reset(new StateData(std::move(exp_data.value())));
30✔
147

148
                        ctx.BeginDeploymentLogging();
30✔
149

150
                        log::Info("Running Mender client " + conf::kMenderVersion);
30✔
151
                        log::Info(
30✔
152
                                "Deployment with ID " + ctx.deployment.state_data->update_info.id + " started.");
60✔
153

154
                        poster.PostEvent(StateEvent::DeploymentStarted);
30✔
155
                        poster.PostEvent(StateEvent::Success);
30✔
156
                });
64✔
157

158
        if (err != error::NoError) {
32✔
159
                log::Error("Error when trying to poll for deployment: " + err.String());
2✔
160
                poster.PostEvent(StateEvent::Failure);
2✔
161
        }
162
}
32✔
163

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

167
        ctx.deployment.state_data->state = DatabaseStateString();
215✔
168

169
        auto err = ctx.SaveDeploymentStateData(*ctx.deployment.state_data);
215✔
170
        if (err != error::NoError) {
215✔
171
                log::Error(err.String());
8✔
172
                if (err.code
8✔
173
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
8✔
174
                        poster.PostEvent(StateEvent::StateLoopDetected);
1✔
175
                        return;
1✔
176
                } else if (!IsFailureState()) {
7✔
177
                        // Non-failure states should be interrupted, but failure states should be
178
                        // allowed to do their work, even if a database error was detected.
179
                        poster.PostEvent(StateEvent::Failure);
1✔
180
                        return;
1✔
181
                }
182
        }
183

184
        OnEnterSaveState(ctx, poster);
213✔
185
}
186

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

190
        auto req = make_shared<http::OutgoingRequest>();
30✔
191
        req->SetMethod(http::Method::GET);
30✔
192
        auto err = req->SetAddress(ctx.deployment.state_data->update_info.artifact.source.uri);
30✔
193
        if (err != error::NoError) {
30✔
194
                log::Error(err.String());
×
195
                poster.PostEvent(StateEvent::Failure);
×
196
                return;
×
197
        }
198

199
        err = ctx.download_client.AsyncCall(
30✔
200
                req,
201
                [&ctx, &poster](http::ExpectedIncomingResponsePtr exp_resp) {
119✔
202
                        if (!exp_resp) {
30✔
203
                                log::Error(exp_resp.error().String());
×
204
                                poster.PostEvent(StateEvent::Failure);
×
205
                                return;
1✔
206
                        }
207

208
                        auto &resp = exp_resp.value();
30✔
209
                        if (resp->GetStatusCode() != http::StatusOK) {
30✔
210
                                log::Error(
1✔
211
                                        "Unexpected status code while fetching artifact: " + resp->GetStatusMessage());
2✔
212
                                ctx.download_client.Cancel();
1✔
213
                                poster.PostEvent(StateEvent::Failure);
1✔
214
                                return;
1✔
215
                        }
216

217
                        auto http_reader = resp->MakeBodyAsyncReader();
58✔
218
                        ctx.deployment.artifact_reader =
219
                                make_shared<events::io::ReaderFromAsyncReader>(ctx.event_loop, http_reader);
29✔
220
                        ParseArtifact(ctx, poster);
29✔
221
                },
222
                [](http::ExpectedIncomingResponsePtr exp_resp) {
29✔
223
                        if (!exp_resp) {
29✔
224
                                log::Error(exp_resp.error().String());
×
225
                                // Cannot handle error here, because this handler is called at the
226
                                // end of the download, when we have already left this state. So
227
                                // rely on this error being propagated through the BodyAsyncReader
228
                                // above instead.
229
                                return;
×
230
                        }
231
                });
60✔
232

233
        if (err != error::NoError) {
30✔
234
                log::Error(err.String());
×
235
                poster.PostEvent(StateEvent::Failure);
×
236
                return;
×
237
        }
238
}
239

240
void UpdateDownloadState::ParseArtifact(Context &ctx, sm::EventPoster<StateEvent> &poster) {
29✔
241
        artifact::config::ParserConfig config {
29✔
242
                .artifact_scripts_filesystem_path =
243
                        ctx.mender_context.GetConfig().paths.GetArtScriptsPath(),
29✔
244
                .artifact_scripts_version = 3,
245
        };
29✔
246
        auto exp_parser = artifact::Parse(*ctx.deployment.artifact_reader, config);
29✔
247
        if (!exp_parser) {
29✔
248
                log::Error(exp_parser.error().String());
×
249
                poster.PostEvent(StateEvent::Failure);
×
250
                return;
×
251
        }
252
        ctx.deployment.artifact_parser.reset(new artifact::Artifact(std::move(exp_parser.value())));
29✔
253

254
        auto exp_header = artifact::View(*ctx.deployment.artifact_parser, 0);
29✔
255
        if (!exp_header) {
29✔
256
                log::Error(exp_header.error().String());
×
257
                poster.PostEvent(StateEvent::Failure);
×
258
                return;
×
259
        }
260
        auto &header = exp_header.value();
29✔
261

262
        auto exp_matches = ctx.mender_context.MatchesArtifactDepends(header.header);
29✔
263
        if (!exp_matches) {
29✔
264
                log::Error(exp_matches.error().String());
2✔
265
                poster.PostEvent(StateEvent::Failure);
2✔
266
                return;
2✔
267
        } else if (!exp_matches.value()) {
27✔
268
                // reasons already logged
269
                poster.PostEvent(StateEvent::Failure);
1✔
270
                return;
1✔
271
        }
272

273
        log::Info("Installing artifact...");
26✔
274

275
        ctx.deployment.state_data->FillUpdateDataFromArtifact(header);
26✔
276

277
        ctx.deployment.state_data->state = Context::kUpdateStateDownload;
26✔
278

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

281
        // Initial state data save, now that we have enough information from the artifact.
282
        auto err = ctx.SaveDeploymentStateData(*ctx.deployment.state_data);
26✔
283
        if (err != error::NoError) {
26✔
284
                log::Error(err.String());
1✔
285
                if (err.code
1✔
286
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
1✔
287
                        poster.PostEvent(StateEvent::StateLoopDetected);
×
288
                        return;
×
289
                } else {
290
                        poster.PostEvent(StateEvent::Failure);
1✔
291
                        return;
1✔
292
                }
293
        }
294

295
        if (header.header.payload_type == "") {
25✔
296
                // Empty-payload-artifact, aka "bootstrap artifact".
297
                poster.PostEvent(StateEvent::NothingToDo);
1✔
298
                return;
1✔
299
        }
300

301
        ctx.deployment.update_module.reset(
24✔
302
                new update_module::UpdateModule(ctx.mender_context, header.header.payload_type));
24✔
303

304
        err = ctx.deployment.update_module->CleanAndPrepareFileTree(
24✔
305
                ctx.deployment.update_module->GetUpdateModuleWorkDir(), header);
48✔
306
        if (err != error::NoError) {
24✔
307
                log::Error(err.String());
×
308
                poster.PostEvent(StateEvent::Failure);
×
309
                return;
×
310
        }
311

312
        auto exp_payload = ctx.deployment.artifact_parser->Next();
24✔
313
        if (!exp_payload) {
24✔
314
                log::Error(exp_payload.error().String());
×
315
                poster.PostEvent(StateEvent::Failure);
×
316
                return;
×
317
        }
318
        ctx.deployment.artifact_payload.reset(new artifact::Payload(std::move(exp_payload.value())));
24✔
319

320
        ctx.deployment.update_module->AsyncDownload(
48✔
321
                ctx.event_loop, *ctx.deployment.artifact_payload, [&poster](error::Error err) {
24✔
322
                        if (err != error::NoError) {
24✔
323
                                log::Error(err.String());
2✔
324
                                poster.PostEvent(StateEvent::Failure);
2✔
325
                                return;
2✔
326
                        }
327

328
                        poster.PostEvent(StateEvent::Success);
22✔
329
                });
48✔
330
}
331

332
SendStatusUpdateState::SendStatusUpdateState(
×
333
        optional::optional<deployments::DeploymentStatus> status) :
144✔
334
        status_(status),
335
        mode_(FailureMode::Ignore) {
×
336
}
×
337

338
SendStatusUpdateState::SendStatusUpdateState(
×
339
        optional::optional<deployments::DeploymentStatus> status,
340
        events::EventLoop &event_loop,
341
        int retry_interval_seconds) :
96✔
342
        status_(status),
343
        mode_(FailureMode::RetryThenFail),
344
        // MEN-2676: Cap at 10 retries.
345
        retry_(
346
                Retry {http::ExponentialBackoff(chrono::seconds(retry_interval_seconds), 10), event_loop}) {
×
347
}
×
348

349
void SendStatusUpdateState::SetSmallestWaitInterval(chrono::milliseconds interval) {
88✔
350
        if (retry_) {
88✔
351
                retry_->backoff.SetSmallestInterval(interval);
88✔
352
        }
353
}
88✔
354

355
void SendStatusUpdateState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
121✔
356
        // Reset this every time we enter the state, which means a new round of retries.
357
        if (retry_) {
121✔
358
                retry_->backoff.Reset();
56✔
359
        }
360

361
        DoStatusUpdate(ctx, poster);
121✔
362
}
121✔
363

364
void SendStatusUpdateState::DoStatusUpdate(Context &ctx, sm::EventPoster<StateEvent> &poster) {
140✔
365
        assert(ctx.deployment_client);
140✔
366
        assert(ctx.deployment.state_data);
140✔
367

368
        log::Info("Sending status update to server");
140✔
369

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

374
                        switch (mode_) {
24✔
375
                        case FailureMode::Ignore:
3✔
376
                                break;
3✔
377
                        case FailureMode::RetryThenFail:
21✔
378
                                if (err.code
21✔
379
                                        == deployments::MakeError(deployments::DeploymentAbortedError, "").code) {
21✔
380
                                        // If the deployment was aborted upstream it is an immediate
381
                                        // failure, even if retry is enabled.
382
                                        poster.PostEvent(StateEvent::Failure);
1✔
383
                                        return;
21✔
384
                                }
385

386
                                auto exp_interval = retry_->backoff.NextInterval();
40✔
387
                                if (!exp_interval) {
20✔
388
                                        log::Error(
1✔
389
                                                "Giving up on sending status updates to server: "
390
                                                + exp_interval.error().String());
2✔
391
                                        poster.PostEvent(StateEvent::Failure);
1✔
392
                                        return;
1✔
393
                                }
394

395
                                log::Info(
19✔
396
                                        "Retrying status update after "
397
                                        + to_string(chrono::milliseconds(*exp_interval).count() / 1000) + " seconds");
38✔
398

399
                                retry_->wait_timer.AsyncWait(
19✔
400
                                        *exp_interval, [this, &ctx, &poster](error::Error err) {
19✔
401
                                                // Error here is quite unexpected (from a timer), so treat
402
                                                // this as an immediate error, despite Retry flag.
403
                                                if (err != error::NoError) {
19✔
404
                                                        log::Error(
×
405
                                                                "Unexpected error in SendStatusUpdateState wait timer: "
406
                                                                + err.String());
×
407
                                                        poster.PostEvent(StateEvent::Failure);
×
408
                                                        return;
×
409
                                                }
410

411
                                                // Try again. Since both status and logs are sent
412
                                                // from here, there's a chance this might resubmit
413
                                                // the status, but there's no harm in it, and it
414
                                                // won't happen often.
415
                                                DoStatusUpdate(ctx, poster);
19✔
416
                                        });
38✔
417
                                return;
19✔
418
                        }
419
                }
420

421
                poster.PostEvent(StateEvent::Success);
119✔
422
        };
140✔
423

424
        deployments::DeploymentStatus status;
425
        if (status_) {
140✔
426
                status = status_.value();
94✔
427
        } else {
428
                // If nothing is specified, grab success/failure status from the deployment status.
429
                if (ctx.deployment.failed) {
46✔
430
                        status = deployments::DeploymentStatus::Failure;
39✔
431
                } else {
432
                        status = deployments::DeploymentStatus::Success;
7✔
433
                }
434
        }
435

436
        // Push status.
437
        auto err = ctx.deployment_client->PushStatus(
140✔
438
                ctx.deployment.state_data->update_info.id,
140✔
439
                status,
440
                "",
441
                ctx.mender_context.GetConfig().server_url,
280✔
442
                ctx.http_client,
443
                [result_handler, &ctx](error::Error err) {
412✔
444
                        // If there is an error, we don't submit logs now, but call the handler,
445
                        // which may schedule a retry later. If there is no error, and the
446
                        // deployment as a whole was successful, then also call the handler here,
447
                        // since we don't need to submit logs at all then.
448
                        if (err != error::NoError || !ctx.deployment.failed) {
140✔
449
                                result_handler(err);
101✔
450
                                return;
101✔
451
                        }
452

453
                        // Push logs.
454
                        err = ctx.deployment_client->PushLogs(
78✔
455
                                ctx.deployment.state_data->update_info.id,
39✔
456
                                ctx.deployment.logger->LogFilePath(),
78✔
457
                                ctx.mender_context.GetConfig().server_url,
39✔
458
                                ctx.http_client,
459
                                result_handler);
117✔
460

461
                        if (err != error::NoError) {
39✔
462
                                result_handler(err);
×
463
                        }
464
                });
560✔
465

466
        if (err != error::NoError) {
140✔
467
                result_handler(err);
×
468
        }
469

470
        // No action, wait for reply from status endpoint.
471
}
140✔
472

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

476
        DefaultAsyncErrorHandler(
21✔
477
                poster,
478
                ctx.deployment.update_module->AsyncArtifactInstall(
21✔
479
                        ctx.event_loop, DefaultStateHandler {poster}));
42✔
480
}
21✔
481

482
void UpdateCheckRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
36✔
483
        DefaultAsyncErrorHandler(
36✔
484
                poster,
485
                ctx.deployment.update_module->AsyncNeedsReboot(
36✔
486
                        ctx.event_loop, [&ctx, &poster](update_module::ExpectedRebootAction reboot_action) {
140✔
487
                                if (!reboot_action.has_value()) {
36✔
488
                                        log::Error(reboot_action.error().String());
2✔
489
                                        poster.PostEvent(StateEvent::Failure);
2✔
490
                                        return;
2✔
491
                                }
492

493
                                ctx.deployment.state_data->update_info.reboot_requested.resize(1);
34✔
494
                                ctx.deployment.state_data->update_info.reboot_requested[0] =
34✔
495
                                        NeedsRebootToDbString(*reboot_action);
68✔
496
                                switch (*reboot_action) {
34✔
497
                                case update_module::RebootAction::No:
4✔
498
                                        poster.PostEvent(StateEvent::NothingToDo);
4✔
499
                                        break;
4✔
500
                                case update_module::RebootAction::Yes:
30✔
501
                                case update_module::RebootAction::Automatic:
502
                                        poster.PostEvent(StateEvent::Success);
30✔
503
                                        break;
30✔
504
                                }
505
                        }));
72✔
506
}
36✔
507

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

511
        assert(ctx.deployment.state_data->update_info.reboot_requested.size() == 1);
13✔
512
        auto exp_reboot_mode =
513
                DbStringToNeedsReboot(ctx.deployment.state_data->update_info.reboot_requested[0]);
26✔
514
        // Should always be true because we check it at load time.
515
        assert(exp_reboot_mode);
13✔
516

517
        switch (exp_reboot_mode.value()) {
13✔
518
        case update_module::RebootAction::No:
×
519
                // Should not happen because then we don't enter this state.
520
                assert(false);
×
521
                poster.PostEvent(StateEvent::Failure);
522
                break;
523
        case update_module::RebootAction::Yes:
13✔
524
                DefaultAsyncErrorHandler(
13✔
525
                        poster,
526
                        ctx.deployment.update_module->AsyncArtifactReboot(
13✔
527
                                ctx.event_loop, DefaultStateHandler {poster}));
26✔
528
                break;
13✔
529
        case update_module::RebootAction::Automatic:
×
530
                DefaultAsyncErrorHandler(
×
531
                        poster,
532
                        ctx.deployment.update_module->AsyncSystemReboot(
×
533
                                ctx.event_loop, DefaultStateHandler {poster}));
×
534
                break;
×
535
        }
536
}
13✔
537

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

541
        DefaultAsyncErrorHandler(
14✔
542
                poster,
543
                ctx.deployment.update_module->AsyncArtifactVerifyReboot(
14✔
544
                        ctx.event_loop, DefaultStateHandler {poster}));
28✔
545
}
14✔
546

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

552
        poster.PostEvent(StateEvent::Success);
10✔
553
}
10✔
554

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

558
        DefaultAsyncErrorHandler(
8✔
559
                poster,
560
                ctx.deployment.update_module->AsyncArtifactCommit(
8✔
561
                        ctx.event_loop, DefaultStateHandler {poster}));
16✔
562
}
8✔
563

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

568
        // Now we have committed. If we had a schema update, re-save state data with the new schema.
569
        assert(ctx.deployment.state_data);
6✔
570
        auto &state_data = *ctx.deployment.state_data;
6✔
571
        if (state_data.update_info.has_db_schema_update) {
6✔
572
                state_data.update_info.has_db_schema_update = false;
1✔
573
                auto err = ctx.SaveDeploymentStateData(state_data);
1✔
574
                if (err != error::NoError) {
1✔
575
                        log::Error("Not able to commit schema update: " + err.String());
×
576
                        poster.PostEvent(StateEvent::Failure);
×
577
                        return;
×
578
                }
579
        }
580

581
        poster.PostEvent(StateEvent::Success);
6✔
582
}
583

584
void UpdateCheckRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
23✔
585
        DefaultAsyncErrorHandler(
23✔
586
                poster,
587
                ctx.deployment.update_module->AsyncSupportsRollback(
23✔
588
                        ctx.event_loop, [&ctx, &poster](expected::ExpectedBool rollback_supported) {
88✔
589
                                if (!rollback_supported.has_value()) {
23✔
590
                                        log::Error(rollback_supported.error().String());
1✔
591
                                        poster.PostEvent(StateEvent::Failure);
1✔
592
                                        return;
1✔
593
                                }
594

595
                                ctx.deployment.state_data->update_info.supports_rollback =
22✔
596
                                        SupportsRollbackToDbString(*rollback_supported);
44✔
597
                                if (*rollback_supported) {
22✔
598
                                        poster.PostEvent(StateEvent::RollbackStarted);
20✔
599
                                        poster.PostEvent(StateEvent::Success);
20✔
600
                                } else {
601
                                        poster.PostEvent(StateEvent::NothingToDo);
2✔
602
                                }
603
                        }));
46✔
604
}
23✔
605

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

609
        DefaultAsyncErrorHandler(
21✔
610
                poster,
611
                ctx.deployment.update_module->AsyncArtifactRollback(
21✔
612
                        ctx.event_loop, DefaultStateHandler {poster}));
42✔
613
}
21✔
614

615
void UpdateRollbackRebootState::OnEnterSaveState(
29✔
616
        Context &ctx, sm::EventPoster<StateEvent> &poster) {
617
        log::Debug("Entering ArtifactRollbackReboot state");
29✔
618

619
        // We ignore errors in this state as long as the ArtifactVerifyRollbackReboot state
620
        // succeeds.
621
        auto err = ctx.deployment.update_module->AsyncArtifactRollbackReboot(
622
                ctx.event_loop, [&poster](error::Error err) {
58✔
623
                        if (err != error::NoError) {
29✔
624
                                log::Error(err.String());
1✔
625
                        }
626
                        poster.PostEvent(StateEvent::Success);
29✔
627
                });
87✔
628

629
        if (err != error::NoError) {
29✔
630
                log::Error(err.String());
×
631
                poster.PostEvent(StateEvent::Success);
×
632
        }
633
}
29✔
634

635
void UpdateVerifyRollbackRebootState::OnEnterSaveState(
30✔
636
        Context &ctx, sm::EventPoster<StateEvent> &poster) {
637
        log::Debug("Entering ArtifactVerifyRollbackReboot state");
30✔
638

639
        // In this state we only retry, we don't fail. If this keeps on going forever, then the
640
        // state loop detection will eventually kick in.
641
        auto err = ctx.deployment.update_module->AsyncArtifactVerifyRollbackReboot(
642
                ctx.event_loop, [&poster](error::Error err) {
60✔
643
                        if (err != error::NoError) {
30✔
644
                                log::Error(err.String());
12✔
645
                                poster.PostEvent(StateEvent::Retry);
12✔
646
                                return;
12✔
647
                        }
648
                        poster.PostEvent(StateEvent::Success);
18✔
649
                });
60✔
650
        if (err != error::NoError) {
30✔
651
                log::Error(err.String());
×
652
                poster.PostEvent(StateEvent::Retry);
×
653
        }
654
}
30✔
655

656
void UpdateRollbackSuccessfulState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
27✔
657
        ctx.deployment.state_data->update_info.all_rollbacks_successful = true;
27✔
658
        poster.PostEvent(StateEvent::Success);
27✔
659
}
27✔
660

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

664
        DefaultAsyncErrorHandler(
27✔
665
                poster,
666
                ctx.deployment.update_module->AsyncArtifactFailure(
27✔
667
                        ctx.event_loop, DefaultStateHandler {poster}));
54✔
668
}
27✔
669

670
static string AddInconsistentSuffix(const string &str) {
8✔
671
        const auto &suffix = main_context::MenderContext::broken_artifact_name_suffix;
8✔
672
        // `string::ends_with` is C++20... grumble
673
        string ret {str};
8✔
674
        if (!common::EndsWith(ret, suffix)) {
8✔
675
                ret.append(suffix);
8✔
676
        }
677
        return ret;
8✔
678
}
679

680
void UpdateSaveProvidesState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
34✔
681
        if (ctx.deployment.failed && !ctx.deployment.rollback_failed) {
34✔
682
                // If the update failed, but we rolled back successfully, then we don't need to do
683
                // anything, just keep the old data.
684
                poster.PostEvent(StateEvent::Success);
21✔
685
                return;
21✔
686
        }
687

688
        assert(ctx.deployment.state_data);
13✔
689
        // This state should never happen: rollback failed, but update not failed??
690
        assert(!(!ctx.deployment.failed && ctx.deployment.rollback_failed));
13✔
691

692
        // We expect Cleanup to be the next state after this.
693
        ctx.deployment.state_data->state = ctx.kUpdateStateCleanup;
13✔
694

695
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
13✔
696

697
        string artifact_name;
13✔
698
        if (ctx.deployment.rollback_failed) {
13✔
699
                artifact_name = AddInconsistentSuffix(artifact.artifact_name);
6✔
700
        } else {
701
                artifact_name = artifact.artifact_name;
7✔
702
        }
703

704
        auto err = ctx.mender_context.CommitArtifactData(
13✔
705
                artifact_name,
706
                artifact.artifact_group,
13✔
707
                artifact.type_info_provides,
13✔
708
                artifact.clears_artifact_provides,
13✔
709
                [&ctx](kv_db::Transaction &txn) {
13✔
710
                        // Save the Cleanup state together with the artifact data, atomically.
711
                        return ctx.SaveDeploymentStateData(txn, *ctx.deployment.state_data);
13✔
712
                });
26✔
713
        if (err != error::NoError) {
13✔
714
                log::Error("Error saving artifact data: " + err.String());
×
715
                if (err.code
×
716
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
×
717
                        poster.PostEvent(StateEvent::StateLoopDetected);
×
718
                        return;
×
719
                }
720
                poster.PostEvent(StateEvent::Failure);
×
721
                return;
×
722
        }
723

724
        poster.PostEvent(StateEvent::Success);
13✔
725
}
726

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

730
        // It's possible for there not to be an initialized update_module structure, if the
731
        // deployment failed before we could successfully parse the artifact. If so, cleanup is a
732
        // no-op.
733
        if (!ctx.deployment.update_module) {
44✔
734
                poster.PostEvent(StateEvent::Success);
6✔
735
                return;
6✔
736
        }
737

738
        DefaultAsyncErrorHandler(
38✔
739
                poster,
740
                ctx.deployment.update_module->AsyncCleanup(ctx.event_loop, DefaultStateHandler {poster}));
76✔
741
}
742

743
void ClearArtifactDataState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
46✔
744
        auto err = ctx.mender_context.GetMenderStoreDB().WriteTransaction([](kv_db::Transaction &txn) {
46✔
745
                // Remove state data, since we're done now.
746
                auto err = txn.Remove(main_context::MenderContext::state_data_key);
88✔
747
                if (err != error::NoError) {
44✔
748
                        return err;
×
749
                }
750
                return txn.Remove(main_context::MenderContext::state_data_key_uncommitted);
44✔
751
        });
46✔
752
        if (err != error::NoError) {
46✔
753
                log::Error("Error removing artifact data: " + err.String());
2✔
754
                poster.PostEvent(StateEvent::Failure);
2✔
755
                return;
2✔
756
        }
757

758
        poster.PostEvent(StateEvent::Success);
44✔
759
}
760

761
void StateLoopState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
2✔
762
        assert(ctx.deployment.state_data);
2✔
763
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
2✔
764

765
        // Mark update as inconsistent.
766
        string artifact_name = AddInconsistentSuffix(artifact.artifact_name);
2✔
767

768
        auto err = ctx.mender_context.CommitArtifactData(
2✔
769
                artifact_name,
770
                artifact.artifact_group,
2✔
771
                artifact.type_info_provides,
2✔
772
                artifact.clears_artifact_provides,
2✔
773
                [](kv_db::Transaction &txn) { return error::NoError; });
6✔
774
        if (err != error::NoError) {
2✔
775
                log::Error("Error saving inconsistent artifact data: " + err.String());
×
776
                poster.PostEvent(StateEvent::Failure);
×
777
                return;
×
778
        }
779

780
        poster.PostEvent(StateEvent::Success);
2✔
781
}
782

783
void EndOfDeploymentState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
46✔
784
        ctx.FinishDeploymentLogging();
46✔
785

786
        ctx.deployment = {};
46✔
787
        poster.PostEvent(StateEvent::DeploymentEnded);
46✔
788
        poster.PostEvent(StateEvent::Success);
46✔
789
}
46✔
790

791
ExitState::ExitState(events::EventLoop &event_loop) :
×
792
        event_loop_(event_loop) {
×
793
}
×
794

795
void ExitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
46✔
796
        event_loop_.Stop();
46✔
797
}
46✔
798

799
namespace deployment_tracking {
800

801
void NoFailuresState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
32✔
802
        ctx.deployment.failed = false;
32✔
803
        ctx.deployment.rollback_failed = false;
32✔
804
}
32✔
805

806
void FailureState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
32✔
807
        ctx.deployment.failed = true;
32✔
808
        ctx.deployment.rollback_failed = true;
32✔
809
}
32✔
810

811
void RollbackAttemptedState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
26✔
812
        ctx.deployment.failed = true;
26✔
813
        ctx.deployment.rollback_failed = false;
26✔
814
}
26✔
815

816
void RollbackFailedState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
6✔
817
        ctx.deployment.failed = true;
6✔
818
        ctx.deployment.rollback_failed = true;
6✔
819
}
6✔
820

821
} // namespace deployment_tracking
822

823
} // namespace daemon
824
} // namespace update
825
} // 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