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

mendersoftware / mender / 1019717806

28 Sep 2023 01:24PM UTC coverage: 78.05% (-0.1%) from 78.153%
1019717806

push

gitlab-ci

kacf
test: Drain event queue before checking number of connections.

One http instance was moved because Boost accesses its members even
after they are destroyed. It shouldn't, but this is unlikely to happen
in production where the loop is continuously running.

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

6749 of 8647 relevant lines covered (78.05%)

10652.94 hits per line

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

81.78
/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 kv_db = mender::common::key_value_database;
33
namespace path = mender::common::path;
34
namespace log = mender::common::log;
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) {
289✔
42
                if (err != error::NoError) {
289✔
43
                        log::Error(err.String());
23✔
44
                        poster.PostEvent(StateEvent::Failure);
23✔
45
                        return;
23✔
46
                }
47
                poster.PostEvent(StateEvent::Success);
266✔
48
        }
49

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

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

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

64
void InitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
57✔
65
        // I will never run - just a placeholder to start the state-machine at
66
        poster.PostEvent(StateEvent::Started); // Start the state machine
57✔
67
}
57✔
68

69
void StateScriptState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
1,026✔
70
        string state_name {script_executor::Name(this->state_, this->action_)};
1,026✔
71
        log::Debug("Executing the  " + state_name + " State Scripts...");
1,026✔
72
        auto err = this->script_.AsyncRunScripts(
73
                this->state_, this->action_, [state_name, &poster](error::Error err) {
2,052✔
74
                        if (err != error::NoError) {
1,026✔
75
                                log::Error(
21✔
76
                                        "Received error: (" + err.String() + ") when running the State Script scripts "
42✔
77
                                        + state_name);
63✔
78
                                poster.PostEvent(StateEvent::Failure);
21✔
79
                                return;
21✔
80
                        }
81
                        log::Debug("Successfully ran the " + state_name + " State Scripts...");
1,005✔
82
                        poster.PostEvent(StateEvent::Success);
1,005✔
83
                });
2,052✔
84

85
        if (err != error::NoError) {
1,026✔
86
                log::Error(
×
87
                        "Failed to schedule the state script execution for: " + state_name
×
88
                        + " got error: " + err.String());
×
89
                poster.PostEvent(StateEvent::Failure);
×
90
                return;
×
91
        }
92
}
93

94

95
void SaveStateScriptState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
274✔
96
        return state_script_state_.OnEnter(ctx, poster);
274✔
97
}
98

99
void IdleState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
116✔
100
        log::Debug("Entering Idle state");
116✔
101
}
116✔
102

103
void SubmitInventoryState::DoSubmitInventory(Context &ctx, sm::EventPoster<StateEvent> &poster) {
57✔
104
        log::Debug("Submitting inventory");
57✔
105

106
        auto handler = [&poster](error::Error err) {
114✔
107
                if (err != error::NoError) {
57✔
108
                        log::Error("Failed to submit inventory: " + err.String());
1✔
109
                }
110
                poster.PostEvent((err == error::NoError) ? StateEvent::Success : StateEvent::Failure);
57✔
111
        };
57✔
112

113
        auto err = ctx.inventory_client->PushData(
57✔
114
                ctx.mender_context.GetConfig().paths.GetInventoryScriptsDir(),
114✔
115
                ctx.mender_context.GetConfig().server_url,
57✔
116
                ctx.event_loop,
117
                ctx.http_client,
118
                handler);
171✔
119

120
        if (err != error::NoError) {
57✔
121
                // This is the only case the handler won't be called for us by
122
                // PushData() (see inventory::PushInventoryData()).
123
                handler(err);
1✔
124
        }
125
}
57✔
126

127
void SubmitInventoryState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
57✔
128
        // Schedule timer for next update first, so that long running submissions do not postpone
129
        // the schedule.
130
        log::Debug(
57✔
131
                "Scheduling the next inventory submission in: "
132
                + to_string(ctx.mender_context.GetConfig().inventory_poll_interval_seconds) + " seconds");
114✔
133
        poll_timer_.AsyncWait(
57✔
134
                chrono::seconds(ctx.mender_context.GetConfig().inventory_poll_interval_seconds),
57✔
135
                [&poster](error::Error err) {
2✔
136
                        if (err != error::NoError) {
1✔
137
                                log::Error("Inventory poll timer caused error: " + err.String());
×
138
                        } else {
139
                                poster.PostEvent(StateEvent::InventoryPollingTriggered);
1✔
140
                        }
141
                });
115✔
142

143
        DoSubmitInventory(ctx, poster);
57✔
144
}
57✔
145

146
void PollForDeploymentState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
57✔
147
        log::Debug("Polling for update");
57✔
148

149
        // Schedule timer for next update first, so that long running submissions do not postpone
150
        // the schedule.
151
        log::Debug(
57✔
152
                "Scheduling the next deployment check in: "
153
                + to_string(ctx.mender_context.GetConfig().update_poll_interval_seconds) + " seconds");
114✔
154
        poll_timer_.AsyncWait(
57✔
155
                chrono::seconds(ctx.mender_context.GetConfig().update_poll_interval_seconds),
57✔
156
                [&poster](error::Error err) {
×
157
                        if (err != error::NoError) {
×
158
                                log::Error("Update poll timer caused error: " + err.String());
×
159
                        } else {
160
                                poster.PostEvent(StateEvent::DeploymentPollingTriggered);
×
161
                        }
162
                });
114✔
163

164
        auto err = ctx.deployment_client->CheckNewDeployments(
57✔
165
                ctx.mender_context,
166
                ctx.mender_context.GetConfig().server_url,
114✔
167
                ctx.http_client,
168
                [&ctx, &poster](mender::update::deployments::CheckUpdatesAPIResponse response) {
330✔
169
                        if (!response) {
55✔
170
                                log::Error("Error while polling for deployment: " + response.error().String());
×
171
                                poster.PostEvent(StateEvent::Failure);
×
172
                                return;
×
173
                        } else if (!response.value()) {
55✔
174
                                log::Info("No update available");
×
175
                                poster.PostEvent(StateEvent::NothingToDo);
×
176
                                return;
×
177
                        }
178

179
                        auto exp_data = ApiResponseJsonToStateData(response.value().value());
55✔
180
                        if (!exp_data) {
55✔
181
                                log::Error("Error in API response: " + exp_data.error().String());
×
182
                                poster.PostEvent(StateEvent::Failure);
×
183
                                return;
×
184
                        }
185

186
                        // Make a new set of update data.
187
                        ctx.deployment.state_data.reset(new StateData(std::move(exp_data.value())));
55✔
188

189
                        ctx.BeginDeploymentLogging();
55✔
190

191
                        log::Info("Running Mender client " + conf::kMenderVersion);
55✔
192
                        log::Info(
55✔
193
                                "Deployment with ID " + ctx.deployment.state_data->update_info.id + " started.");
110✔
194

195
                        poster.PostEvent(StateEvent::DeploymentStarted);
55✔
196
                        poster.PostEvent(StateEvent::Success);
55✔
197
                });
114✔
198

199
        if (err != error::NoError) {
57✔
200
                log::Error("Error when trying to poll for deployment: " + err.String());
2✔
201
                poster.PostEvent(StateEvent::Failure);
2✔
202
        }
203
}
57✔
204

205
void SaveState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
524✔
206
        assert(ctx.deployment.state_data);
524✔
207

208
        ctx.deployment.state_data->state = DatabaseStateString();
524✔
209

210
        log::Trace("Storing deployment state in the DB (database-string):" + DatabaseStateString());
524✔
211

212
        auto err = ctx.SaveDeploymentStateData(*ctx.deployment.state_data);
524✔
213
        if (err != error::NoError) {
524✔
214
                log::Error(err.String());
10✔
215
                if (err.code
10✔
216
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
10✔
217
                        poster.PostEvent(StateEvent::StateLoopDetected);
1✔
218
                        return;
1✔
219
                } else if (!IsFailureState()) {
9✔
220
                        // Non-failure states should be interrupted, but failure states should be
221
                        // allowed to do their work, even if a database error was detected.
222
                        poster.PostEvent(StateEvent::Failure);
2✔
223
                        return;
2✔
224
                }
225
        }
226

227
        OnEnterSaveState(ctx, poster);
521✔
228
}
229

230
void UpdateDownloadState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
53✔
231
        log::Debug("Entering Download state");
53✔
232

233
        auto req = make_shared<http::OutgoingRequest>();
53✔
234
        req->SetMethod(http::Method::GET);
53✔
235
        auto err = req->SetAddress(ctx.deployment.state_data->update_info.artifact.source.uri);
53✔
236
        if (err != error::NoError) {
53✔
237
                log::Error(err.String());
×
238
                poster.PostEvent(StateEvent::Failure);
×
239
                return;
×
240
        }
241

242
        err = ctx.download_client.AsyncCall(
53✔
243
                req,
244
                [&ctx, &poster](http::ExpectedIncomingResponsePtr exp_resp) {
211✔
245
                        if (!exp_resp) {
53✔
246
                                log::Error(exp_resp.error().String());
×
247
                                poster.PostEvent(StateEvent::Failure);
×
248
                                return;
1✔
249
                        }
250

251
                        auto &resp = exp_resp.value();
53✔
252
                        if (resp->GetStatusCode() != http::StatusOK) {
53✔
253
                                log::Error(
1✔
254
                                        "Unexpected status code while fetching artifact: " + resp->GetStatusMessage());
2✔
255
                                ctx.download_client.Cancel();
1✔
256
                                poster.PostEvent(StateEvent::Failure);
1✔
257
                                return;
1✔
258
                        }
259

260
                        auto http_reader = resp->MakeBodyAsyncReader();
52✔
261
                        if (!http_reader) {
52✔
262
                                log::Error(http_reader.error().String());
×
263
                                ctx.download_client.Cancel();
×
264
                                poster.PostEvent(StateEvent::Failure);
×
265
                                return;
×
266
                        }
267
                        ctx.deployment.artifact_reader =
268
                                make_shared<events::io::ReaderFromAsyncReader>(ctx.event_loop, http_reader.value());
52✔
269
                        ParseArtifact(ctx, poster);
52✔
270
                },
271
                [](http::ExpectedIncomingResponsePtr exp_resp) {
53✔
272
                        if (!exp_resp) {
53✔
273
                                log::Error(exp_resp.error().String());
53✔
274
                                // Cannot handle error here, because this handler is called at the
275
                                // end of the download, when we have already left this state. So
276
                                // rely on this error being propagated through the BodyAsyncReader
277
                                // above instead.
278
                                return;
53✔
279
                        }
280
                });
106✔
281

282
        if (err != error::NoError) {
53✔
283
                log::Error(err.String());
×
284
                poster.PostEvent(StateEvent::Failure);
×
285
                return;
×
286
        }
287
}
288

289
void UpdateDownloadState::ParseArtifact(Context &ctx, sm::EventPoster<StateEvent> &poster) {
52✔
290
        artifact::config::ParserConfig config {
52✔
291
                .artifact_scripts_filesystem_path =
292
                        ctx.mender_context.GetConfig().paths.GetArtScriptsPath(),
52✔
293
                .artifact_scripts_version = 3,
294
        };
52✔
295
        auto exp_parser = artifact::Parse(*ctx.deployment.artifact_reader, config);
52✔
296
        if (!exp_parser) {
52✔
297
                log::Error(exp_parser.error().String());
×
298
                poster.PostEvent(StateEvent::Failure);
×
299
                return;
×
300
        }
301
        ctx.deployment.artifact_parser.reset(new artifact::Artifact(std::move(exp_parser.value())));
52✔
302

303
        auto exp_header = artifact::View(*ctx.deployment.artifact_parser, 0);
52✔
304
        if (!exp_header) {
52✔
305
                log::Error(exp_header.error().String());
×
306
                poster.PostEvent(StateEvent::Failure);
×
307
                return;
×
308
        }
309
        auto &header = exp_header.value();
52✔
310

311
        auto exp_matches = ctx.mender_context.MatchesArtifactDepends(header.header);
52✔
312
        if (!exp_matches) {
52✔
313
                log::Error(exp_matches.error().String());
2✔
314
                poster.PostEvent(StateEvent::Failure);
2✔
315
                return;
2✔
316
        } else if (!exp_matches.value()) {
50✔
317
                // reasons already logged
318
                poster.PostEvent(StateEvent::Failure);
1✔
319
                return;
1✔
320
        }
321

322
        log::Info("Installing artifact...");
49✔
323

324
        ctx.deployment.state_data->FillUpdateDataFromArtifact(header);
49✔
325

326
        ctx.deployment.state_data->state = Context::kUpdateStateDownload;
49✔
327

328
        assert(ctx.deployment.state_data->update_info.artifact.payload_types.size() == 1);
49✔
329

330
        // Initial state data save, now that we have enough information from the artifact.
331
        auto err = ctx.SaveDeploymentStateData(*ctx.deployment.state_data);
49✔
332
        if (err != error::NoError) {
49✔
333
                log::Error(err.String());
×
334
                if (err.code
×
335
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
×
336
                        poster.PostEvent(StateEvent::StateLoopDetected);
×
337
                        return;
×
338
                } else {
339
                        poster.PostEvent(StateEvent::Failure);
×
340
                        return;
×
341
                }
342
        }
343

344
        if (header.header.payload_type == "") {
49✔
345
                // Empty-payload-artifact, aka "bootstrap artifact".
346
                poster.PostEvent(StateEvent::NothingToDo);
1✔
347
                return;
1✔
348
        }
349

350
        ctx.deployment.update_module.reset(
48✔
351
                new update_module::UpdateModule(ctx.mender_context, header.header.payload_type));
48✔
352

353
        err = ctx.deployment.update_module->CleanAndPrepareFileTree(
48✔
354
                ctx.deployment.update_module->GetUpdateModuleWorkDir(), header);
96✔
355
        if (err != error::NoError) {
48✔
356
                log::Error(err.String());
×
357
                poster.PostEvent(StateEvent::Failure);
×
358
                return;
×
359
        }
360

361
        err = ctx.deployment.update_module->AsyncProvidePayloadFileSizes(
48✔
362
                ctx.event_loop, [&ctx, &poster](expected::ExpectedBool download_with_sizes) {
96✔
363
                        if (!download_with_sizes.has_value()) {
48✔
364
                                log::Error(download_with_sizes.error().String());
×
365
                                poster.PostEvent(StateEvent::Failure);
×
366
                                return;
×
367
                        }
368
                        ctx.deployment.download_with_sizes = download_with_sizes.value();
48✔
369
                        DoDownload(ctx, poster);
48✔
370
                });
96✔
371

372
        if (err != error::NoError) {
48✔
373
                log::Error(err.String());
×
374
                poster.PostEvent(StateEvent::Failure);
×
375
                return;
×
376
        }
377
}
378

379
void UpdateDownloadState::DoDownload(Context &ctx, sm::EventPoster<StateEvent> &poster) {
48✔
380
        auto exp_payload = ctx.deployment.artifact_parser->Next();
48✔
381
        if (!exp_payload) {
48✔
382
                log::Error(exp_payload.error().String());
×
383
                poster.PostEvent(StateEvent::Failure);
×
384
                return;
×
385
        }
386
        ctx.deployment.artifact_payload.reset(new artifact::Payload(std::move(exp_payload.value())));
48✔
387

388
        auto handler = [&poster](error::Error err) {
96✔
389
                if (err != error::NoError) {
48✔
390
                        log::Error(err.String());
2✔
391
                        poster.PostEvent(StateEvent::Failure);
2✔
392
                        return;
2✔
393
                }
394

395
                poster.PostEvent(StateEvent::Success);
46✔
396
        };
48✔
397

398
        if (ctx.deployment.download_with_sizes) {
48✔
399
                ctx.deployment.update_module->AsyncDownloadWithFileSizes(
2✔
400
                        ctx.event_loop, *ctx.deployment.artifact_payload, handler);
2✔
401
        } else {
402
                ctx.deployment.update_module->AsyncDownload(
94✔
403
                        ctx.event_loop, *ctx.deployment.artifact_payload, handler);
94✔
404
        }
405
}
406

407
SendStatusUpdateState::SendStatusUpdateState(
×
408
        optional::optional<deployments::DeploymentStatus> status) :
273✔
409
        status_(status),
410
        mode_(FailureMode::Ignore) {
×
411
}
×
412

413
SendStatusUpdateState::SendStatusUpdateState(
×
414
        optional::optional<deployments::DeploymentStatus> status,
415
        events::EventLoop &event_loop,
416
        int retry_interval_seconds) :
182✔
417
        status_(status),
418
        mode_(FailureMode::RetryThenFail),
419
        // MEN-2676: Cap at 10 retries.
420
        retry_(
421
                Retry {http::ExponentialBackoff(chrono::seconds(retry_interval_seconds), 10), event_loop}) {
×
422
}
×
423

424
void SendStatusUpdateState::SetSmallestWaitInterval(chrono::milliseconds interval) {
174✔
425
        if (retry_) {
174✔
426
                retry_->backoff.SetSmallestInterval(interval);
174✔
427
        }
428
}
174✔
429

430
void SendStatusUpdateState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
239✔
431
        // Reset this every time we enter the state, which means a new round of retries.
432
        if (retry_) {
239✔
433
                retry_->backoff.Reset();
111✔
434
        }
435

436
        DoStatusUpdate(ctx, poster);
239✔
437
}
239✔
438

439
void SendStatusUpdateState::DoStatusUpdate(Context &ctx, sm::EventPoster<StateEvent> &poster) {
258✔
440
        assert(ctx.deployment_client);
258✔
441
        assert(ctx.deployment.state_data);
258✔
442

443
        log::Info("Sending status update to server");
258✔
444

445
        auto result_handler = [this, &ctx, &poster](const error::Error &err) {
579✔
446
                if (err != error::NoError) {
258✔
447
                        log::Error("Could not send deployment status: " + err.String());
24✔
448

449
                        switch (mode_) {
24✔
450
                        case FailureMode::Ignore:
3✔
451
                                break;
3✔
452
                        case FailureMode::RetryThenFail:
21✔
453
                                if (err.code
21✔
454
                                        == deployments::MakeError(deployments::DeploymentAbortedError, "").code) {
21✔
455
                                        // If the deployment was aborted upstream it is an immediate
456
                                        // failure, even if retry is enabled.
457
                                        poster.PostEvent(StateEvent::Failure);
1✔
458
                                        return;
21✔
459
                                }
460

461
                                auto exp_interval = retry_->backoff.NextInterval();
40✔
462
                                if (!exp_interval) {
20✔
463
                                        log::Error(
1✔
464
                                                "Giving up on sending status updates to server: "
465
                                                + exp_interval.error().String());
2✔
466
                                        poster.PostEvent(StateEvent::Failure);
1✔
467
                                        return;
1✔
468
                                }
469

470
                                log::Info(
19✔
471
                                        "Retrying status update after "
472
                                        + to_string(chrono::milliseconds(*exp_interval).count() / 1000) + " seconds");
38✔
473

474
                                retry_->wait_timer.AsyncWait(
19✔
475
                                        *exp_interval, [this, &ctx, &poster](error::Error err) {
19✔
476
                                                // Error here is quite unexpected (from a timer), so treat
477
                                                // this as an immediate error, despite Retry flag.
478
                                                if (err != error::NoError) {
19✔
479
                                                        log::Error(
×
480
                                                                "Unexpected error in SendStatusUpdateState wait timer: "
481
                                                                + err.String());
×
482
                                                        poster.PostEvent(StateEvent::Failure);
×
483
                                                        return;
×
484
                                                }
485

486
                                                // Try again. Since both status and logs are sent
487
                                                // from here, there's a chance this might resubmit
488
                                                // the status, but there's no harm in it, and it
489
                                                // won't happen often.
490
                                                DoStatusUpdate(ctx, poster);
19✔
491
                                        });
38✔
492
                                return;
19✔
493
                        }
494
                }
495

496
                poster.PostEvent(StateEvent::Success);
237✔
497
        };
258✔
498

499
        deployments::DeploymentStatus status;
500
        if (status_) {
258✔
501
                status = status_.value();
169✔
502
        } else {
503
                // If nothing is specified, grab success/failure status from the deployment status.
504
                if (ctx.deployment.failed) {
89✔
505
                        status = deployments::DeploymentStatus::Failure;
72✔
506
                } else {
507
                        status = deployments::DeploymentStatus::Success;
17✔
508
                }
509
        }
510

511
        // Push status.
512
        log::Debug("Pushing deployment status: " + DeploymentStatusString(status));
258✔
513
        auto err = ctx.deployment_client->PushStatus(
258✔
514
                ctx.deployment.state_data->update_info.id,
258✔
515
                status,
516
                "",
517
                ctx.mender_context.GetConfig().server_url,
516✔
518
                ctx.http_client,
519
                [result_handler, &ctx](error::Error err) {
780✔
520
                        // If there is an error, we don't submit logs now, but call the handler,
521
                        // which may schedule a retry later. If there is no error, and the
522
                        // deployment as a whole was successful, then also call the handler here,
523
                        // since we don't need to submit logs at all then.
524
                        if (err != error::NoError || !ctx.deployment.failed) {
258✔
525
                                result_handler(err);
186✔
526
                                return;
186✔
527
                        }
528

529
                        // Push logs.
530
                        err = ctx.deployment_client->PushLogs(
144✔
531
                                ctx.deployment.state_data->update_info.id,
72✔
532
                                ctx.deployment.logger->LogFilePath(),
144✔
533
                                ctx.mender_context.GetConfig().server_url,
72✔
534
                                ctx.http_client,
535
                                result_handler);
216✔
536

537
                        if (err != error::NoError) {
72✔
538
                                result_handler(err);
×
539
                        }
540
                });
1,032✔
541

542
        if (err != error::NoError) {
258✔
543
                result_handler(err);
×
544
        }
545

546
        // No action, wait for reply from status endpoint.
547
}
258✔
548

549
void UpdateInstallState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
42✔
550
        log::Debug("Entering ArtifactInstall state");
42✔
551

552
        DefaultAsyncErrorHandler(
42✔
553
                poster,
554
                ctx.deployment.update_module->AsyncArtifactInstall(
42✔
555
                        ctx.event_loop, DefaultStateHandler {poster}));
84✔
556
}
42✔
557

558
void UpdateCheckRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
72✔
559
        DefaultAsyncErrorHandler(
72✔
560
                poster,
561
                ctx.deployment.update_module->AsyncNeedsReboot(
72✔
562
                        ctx.event_loop, [&ctx, &poster](update_module::ExpectedRebootAction reboot_action) {
284✔
563
                                if (!reboot_action.has_value()) {
72✔
564
                                        log::Error(reboot_action.error().String());
2✔
565
                                        poster.PostEvent(StateEvent::Failure);
2✔
566
                                        return;
2✔
567
                                }
568

569
                                ctx.deployment.state_data->update_info.reboot_requested.resize(1);
70✔
570
                                ctx.deployment.state_data->update_info.reboot_requested[0] =
70✔
571
                                        NeedsRebootToDbString(*reboot_action);
140✔
572
                                switch (*reboot_action) {
70✔
573
                                case update_module::RebootAction::No:
8✔
574
                                        poster.PostEvent(StateEvent::NothingToDo);
8✔
575
                                        break;
8✔
576
                                case update_module::RebootAction::Yes:
62✔
577
                                case update_module::RebootAction::Automatic:
578
                                        poster.PostEvent(StateEvent::Success);
62✔
579
                                        break;
62✔
580
                                }
581
                        }));
144✔
582
}
72✔
583

584
void UpdateRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
27✔
585
        log::Debug("Entering ArtifactReboot state");
27✔
586

587
        assert(ctx.deployment.state_data->update_info.reboot_requested.size() == 1);
27✔
588
        auto exp_reboot_mode =
589
                DbStringToNeedsReboot(ctx.deployment.state_data->update_info.reboot_requested[0]);
54✔
590
        // Should always be true because we check it at load time.
591
        assert(exp_reboot_mode);
27✔
592

593
        switch (exp_reboot_mode.value()) {
27✔
594
        case update_module::RebootAction::No:
×
595
                // Should not happen because then we don't enter this state.
596
                assert(false);
×
597
                poster.PostEvent(StateEvent::Failure);
598
                break;
599
        case update_module::RebootAction::Yes:
27✔
600
                DefaultAsyncErrorHandler(
27✔
601
                        poster,
602
                        ctx.deployment.update_module->AsyncArtifactReboot(
27✔
603
                                ctx.event_loop, DefaultStateHandler {poster}));
54✔
604
                break;
27✔
605
        case update_module::RebootAction::Automatic:
×
606
                DefaultAsyncErrorHandler(
×
607
                        poster,
608
                        ctx.deployment.update_module->AsyncSystemReboot(
×
609
                                ctx.event_loop, DefaultStateHandler {poster}));
×
610
                break;
×
611
        }
612
}
27✔
613

614
void UpdateVerifyRebootState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
29✔
615
        log::Debug("Entering ArtifactVerifyReboot state");
29✔
616

617
        DefaultAsyncErrorHandler(
29✔
618
                poster,
619
                ctx.deployment.update_module->AsyncArtifactVerifyReboot(
29✔
620
                        ctx.event_loop, DefaultStateHandler {poster}));
58✔
621
}
29✔
622

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

628
        poster.PostEvent(StateEvent::Success);
22✔
629
}
22✔
630

631
void UpdateCommitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
18✔
632
        log::Debug("Entering ArtifactCommit state");
18✔
633

634
        DefaultAsyncErrorHandler(
18✔
635
                poster,
636
                ctx.deployment.update_module->AsyncArtifactCommit(
18✔
637
                        ctx.event_loop, DefaultStateHandler {poster}));
36✔
638
}
18✔
639

640
void UpdateAfterCommitState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
18✔
641
        // Now we have committed. If we had a schema update, re-save state data with the new schema.
642
        assert(ctx.deployment.state_data);
18✔
643
        auto &state_data = *ctx.deployment.state_data;
18✔
644
        if (state_data.update_info.has_db_schema_update) {
18✔
645
                state_data.update_info.has_db_schema_update = false;
1✔
646
                auto err = ctx.SaveDeploymentStateData(state_data);
1✔
647
                if (err != error::NoError) {
1✔
648
                        log::Error("Not able to commit schema update: " + err.String());
×
649
                        poster.PostEvent(StateEvent::Failure);
×
650
                        return;
×
651
                }
652
        }
653

654
        poster.PostEvent(StateEvent::Success);
18✔
655
}
656

657
void UpdateCheckRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
44✔
658
        DefaultAsyncErrorHandler(
44✔
659
                poster,
660
                ctx.deployment.update_module->AsyncSupportsRollback(
44✔
661
                        ctx.event_loop, [&ctx, &poster](expected::ExpectedBool rollback_supported) {
168✔
662
                                if (!rollback_supported.has_value()) {
44✔
663
                                        log::Error(rollback_supported.error().String());
1✔
664
                                        poster.PostEvent(StateEvent::Failure);
1✔
665
                                        return;
1✔
666
                                }
667

668
                                ctx.deployment.state_data->update_info.supports_rollback =
43✔
669
                                        SupportsRollbackToDbString(*rollback_supported);
86✔
670
                                if (*rollback_supported) {
43✔
671
                                        poster.PostEvent(StateEvent::RollbackStarted);
37✔
672
                                        poster.PostEvent(StateEvent::Success);
37✔
673
                                } else {
674
                                        poster.PostEvent(StateEvent::NothingToDo);
6✔
675
                                }
676
                        }));
88✔
677
}
44✔
678

679
void UpdateRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
40✔
680
        log::Debug("Entering ArtifactRollback state");
40✔
681

682
        DefaultAsyncErrorHandler(
40✔
683
                poster,
684
                ctx.deployment.update_module->AsyncArtifactRollback(
40✔
685
                        ctx.event_loop, DefaultStateHandler {poster}));
80✔
686
}
40✔
687

688
void UpdateRollbackRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
56✔
689
        log::Debug("Entering ArtifactRollbackReboot state");
56✔
690

691
        // We ignore errors in this state as long as the ArtifactVerifyRollbackReboot state
692
        // succeeds.
693
        auto err = ctx.deployment.update_module->AsyncArtifactRollbackReboot(
694
                ctx.event_loop, [&poster](error::Error err) {
112✔
695
                        if (err != error::NoError) {
56✔
696
                                log::Error(err.String());
1✔
697
                        }
698
                        poster.PostEvent(StateEvent::Success);
56✔
699
                });
168✔
700

701
        if (err != error::NoError) {
56✔
702
                log::Error(err.String());
×
703
                poster.PostEvent(StateEvent::Success);
×
704
        }
705
}
56✔
706

707
void UpdateVerifyRollbackRebootState::OnEnterSaveState(
59✔
708
        Context &ctx, sm::EventPoster<StateEvent> &poster) {
709
        log::Debug("Entering ArtifactVerifyRollbackReboot state");
59✔
710

711
        // In this state we only retry, we don't fail. If this keeps on going forever, then the
712
        // state loop detection will eventually kick in.
713
        auto err = ctx.deployment.update_module->AsyncArtifactVerifyRollbackReboot(
714
                ctx.event_loop, [&poster](error::Error err) {
118✔
715
                        if (err != error::NoError) {
59✔
716
                                log::Error(err.String());
22✔
717
                                poster.PostEvent(StateEvent::Retry);
22✔
718
                                return;
22✔
719
                        }
720
                        poster.PostEvent(StateEvent::Success);
37✔
721
                });
118✔
722
        if (err != error::NoError) {
59✔
723
                log::Error(err.String());
×
724
                poster.PostEvent(StateEvent::Retry);
×
725
        }
726
}
59✔
727

728
void UpdateRollbackSuccessfulState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
49✔
729
        ctx.deployment.state_data->update_info.all_rollbacks_successful = true;
49✔
730
        poster.PostEvent(StateEvent::Success);
49✔
731
}
49✔
732

733
void UpdateFailureState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
54✔
734
        log::Debug("Entering ArtifactFailure state");
54✔
735

736
        DefaultAsyncErrorHandler(
54✔
737
                poster,
738
                ctx.deployment.update_module->AsyncArtifactFailure(
54✔
739
                        ctx.event_loop, DefaultStateHandler {poster}));
108✔
740
}
54✔
741

742
static string AddInconsistentSuffix(const string &str) {
20✔
743
        const auto &suffix = main_context::MenderContext::broken_artifact_name_suffix;
20✔
744
        // `string::ends_with` is C++20... grumble
745
        string ret {str};
20✔
746
        if (!common::EndsWith(ret, suffix)) {
20✔
747
                ret.append(suffix);
20✔
748
        }
749
        return ret;
20✔
750
}
751

752
void UpdateSaveProvidesState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
73✔
753
        if (ctx.deployment.failed && !ctx.deployment.rollback_failed) {
73✔
754
                // If the update failed, but we rolled back successfully, then we don't need to do
755
                // anything, just keep the old data.
756
                poster.PostEvent(StateEvent::Success);
38✔
757
                return;
38✔
758
        }
759

760
        assert(ctx.deployment.state_data);
35✔
761
        // This state should never happen: rollback failed, but update not failed??
762
        assert(!(!ctx.deployment.failed && ctx.deployment.rollback_failed));
35✔
763

764
        // We expect Cleanup to be the next state after this.
765
        ctx.deployment.state_data->state = ctx.kUpdateStateCleanup;
35✔
766

767
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
35✔
768

769
        string artifact_name;
35✔
770
        if (ctx.deployment.rollback_failed) {
35✔
771
                artifact_name = AddInconsistentSuffix(artifact.artifact_name);
18✔
772
        } else {
773
                artifact_name = artifact.artifact_name;
17✔
774
        }
775

776
        auto err = ctx.mender_context.CommitArtifactData(
35✔
777
                artifact_name,
778
                artifact.artifact_group,
35✔
779
                artifact.type_info_provides,
35✔
780
                artifact.clears_artifact_provides,
35✔
781
                [&ctx](kv_db::Transaction &txn) {
35✔
782
                        // Save the Cleanup state together with the artifact data, atomically.
783
                        return ctx.SaveDeploymentStateData(txn, *ctx.deployment.state_data);
35✔
784
                });
70✔
785
        if (err != error::NoError) {
35✔
786
                log::Error("Error saving artifact data: " + err.String());
×
787
                if (err.code
×
788
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
×
789
                        poster.PostEvent(StateEvent::StateLoopDetected);
×
790
                        return;
×
791
                }
792
                poster.PostEvent(StateEvent::Failure);
×
793
                return;
×
794
        }
795

796
        poster.PostEvent(StateEvent::Success);
35✔
797
}
798

799
void UpdateCleanupState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
87✔
800
        log::Debug("Entering ArtifactCleanup state");
87✔
801

802
        // It's possible for there not to be an initialized update_module structure, if the
803
        // deployment failed before we could successfully parse the artifact. If so, cleanup is a
804
        // no-op.
805
        if (!ctx.deployment.update_module) {
87✔
806
                poster.PostEvent(StateEvent::Success);
8✔
807
                return;
8✔
808
        }
809

810
        DefaultAsyncErrorHandler(
79✔
811
                poster,
812
                ctx.deployment.update_module->AsyncCleanup(ctx.event_loop, DefaultStateHandler {poster}));
158✔
813
}
814

815
void ClearArtifactDataState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
89✔
816
        auto err = ctx.mender_context.GetMenderStoreDB().WriteTransaction([](kv_db::Transaction &txn) {
89✔
817
                // Remove state data, since we're done now.
818
                auto err = txn.Remove(main_context::MenderContext::state_data_key);
174✔
819
                if (err != error::NoError) {
87✔
820
                        return err;
×
821
                }
822
                return txn.Remove(main_context::MenderContext::state_data_key_uncommitted);
87✔
823
        });
89✔
824
        if (err != error::NoError) {
89✔
825
                log::Error("Error removing artifact data: " + err.String());
2✔
826
                poster.PostEvent(StateEvent::Failure);
2✔
827
                return;
2✔
828
        }
829

830
        poster.PostEvent(StateEvent::Success);
87✔
831
}
832

833
void StateLoopState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
2✔
834
        assert(ctx.deployment.state_data);
2✔
835
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
2✔
836

837
        // Mark update as inconsistent.
838
        string artifact_name = AddInconsistentSuffix(artifact.artifact_name);
2✔
839

840
        auto err = ctx.mender_context.CommitArtifactData(
2✔
841
                artifact_name,
842
                artifact.artifact_group,
2✔
843
                artifact.type_info_provides,
2✔
844
                artifact.clears_artifact_provides,
2✔
845
                [](kv_db::Transaction &txn) { return error::NoError; });
6✔
846
        if (err != error::NoError) {
2✔
847
                log::Error("Error saving inconsistent artifact data: " + err.String());
×
848
                poster.PostEvent(StateEvent::Failure);
×
849
                return;
×
850
        }
851

852
        poster.PostEvent(StateEvent::Success);
2✔
853
}
854

855
void EndOfDeploymentState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
89✔
856
        ctx.FinishDeploymentLogging();
89✔
857

858
        ctx.deployment = {};
89✔
859
        poster.PostEvent(StateEvent::DeploymentEnded);
89✔
860
        poster.PostEvent(StateEvent::Success);
89✔
861
}
89✔
862

863
ExitState::ExitState(events::EventLoop &event_loop) :
×
864
        event_loop_(event_loop) {
×
865
}
×
866

867
void ExitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
89✔
868
        event_loop_.Stop();
89✔
869
}
89✔
870

871
namespace deployment_tracking {
872

873
void NoFailuresState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
60✔
874
        ctx.deployment.failed = false;
60✔
875
        ctx.deployment.rollback_failed = false;
60✔
876
}
60✔
877

878
void FailureState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
57✔
879
        ctx.deployment.failed = true;
57✔
880
        ctx.deployment.rollback_failed = true;
57✔
881
}
57✔
882

883
void RollbackAttemptedState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
51✔
884
        ctx.deployment.failed = true;
51✔
885
        ctx.deployment.rollback_failed = false;
51✔
886
}
51✔
887

888
void RollbackFailedState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
12✔
889
        ctx.deployment.failed = true;
12✔
890
        ctx.deployment.rollback_failed = true;
12✔
891
}
12✔
892

893
} // namespace deployment_tracking
894

895
} // namespace daemon
896
} // namespace update
897
} // 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