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

mendersoftware / mender / 1012473002

21 Sep 2023 02:17PM UTC coverage: 78.107% (-0.3%) from 78.44%
1012473002

push

gitlab-ci

lluiscampos
chore: log stdout output on errors when parsing UM yes/no commands

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

2 of 2 new or added lines in 1 file covered. (100.0%)

6468 of 8281 relevant lines covered (78.11%)

11106.28 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