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

mendersoftware / mender / 2291262117

28 Jan 2026 10:00AM UTC coverage: 81.518% (+0.04%) from 81.475%
2291262117

push

gitlab-ci

web-flow
Merge pull request #1892 from lluiscampos/fix-build

chore: Fix build after bad merge

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

169 existing lines in 5 files now uncovered.

8874 of 10886 relevant lines covered (81.52%)

20156.53 hits per line

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

81.74
/src/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 <client_shared/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::client_shared::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) {
294✔
42
                if (err != error::NoError) {
294✔
43
                        log::Error(err.String());
23✔
44
                        poster.PostEvent(StateEvent::Failure);
23✔
45
                        return;
23✔
46
                }
47
                poster.PostEvent(StateEvent::Success);
271✔
48
        }
49

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

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

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

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

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

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

97

98
void SaveStateScriptState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
278✔
99
        return state_script_state_.OnEnter(ctx, poster);
278✔
100
}
101

102
void IdleState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
123✔
103
        log::Debug("Entering Idle state");
123✔
104
}
123✔
105

106
ScheduleNextPollState::ScheduleNextPollState(
192✔
107
        events::Timer &timer, const string &poll_action, const StateEvent event, int interval) :
192✔
108
        timer_ {timer},
192✔
109
        poll_action_ {poll_action},
192✔
110
        event_ {event},
192✔
111
        interval_ {interval} {
192✔
112
}
192✔
113

114
void ScheduleNextPollState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
122✔
115
        log::Debug("Scheduling the next " + poll_action_ + " in: " + to_string(interval_) + " seconds");
244✔
116
        timer_.AsyncWait(chrono::seconds(interval_), [this, &poster](error::Error err) {
122✔
117
                if (err != error::NoError) {
8✔
118
                        if (err.code != make_error_condition(errc::operation_canceled)) {
5✔
119
                                log::Error("Timer caused error: " + err.String());
×
120
                        }
121
                } else {
122
                        poster.PostEvent(event_);
3✔
123
                }
124
        });
8✔
125

126
        poster.PostEvent(StateEvent::Success);
122✔
127
}
122✔
128

129
SubmitInventoryState::SubmitInventoryState(int retry_interval_seconds, int retry_count) :
192✔
130
        backoff_ {chrono::seconds(retry_interval_seconds), retry_count} {
96✔
131
}
192✔
132

133
void SubmitInventoryState::HandlePollingError(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
134
        // When using short polling intervals, we should adjust the backoff to ensure
135
        // that the intervals do not exceed the maximum retry polling interval, which
136
        // converts the backoff to a fixed interval.
137
        chrono::milliseconds max_interval =
138
                chrono::seconds(ctx.mender_context.GetConfig().retry_poll_interval_seconds);
×
139
        if (max_interval < backoff_.SmallestInterval()) {
×
140
                backoff_.SetSmallestInterval(max_interval);
×
141
                backoff_.SetMaxInterval(max_interval);
×
142
        }
143
        auto exp_interval = backoff_.NextInterval();
×
144
        if (!exp_interval) {
×
145
                log::Debug(
×
146
                        "Not retrying with backoff, retrying InventoryPollIntervalSeconds: "
147
                        + exp_interval.error().String());
×
148
                return;
149
        }
150
        log::Info(
×
151
                "Retrying inventory polling in "
152
                + to_string(chrono::duration_cast<chrono::seconds>(*exp_interval).count()) + " seconds");
×
153

154
        ctx.inventory_timer.Cancel();
×
155
        ctx.inventory_timer.AsyncWait(*exp_interval, [&poster](error::Error err) {
×
156
                if (err != error::NoError) {
×
157
                        if (err.code != make_error_condition(errc::operation_canceled)) {
×
158
                                log::Error("Retry poll timer caused error: " + err.String());
×
159
                        }
160
                } else {
161
                        poster.PostEvent(StateEvent::InventoryPollingTriggered);
×
162
                }
163
        });
×
164
}
165

166
void SubmitInventoryState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
59✔
167
        log::Debug("Submitting inventory");
59✔
168

169
        auto handler = [this, &ctx, &poster](error::Error err) {
59✔
170
                if (err != error::NoError) {
59✔
171
                        log::Error("Failed to submit inventory: " + err.String());
×
172
                        // Replace the inventory poll timer with a backoff
173
                        HandlePollingError(ctx, poster);
×
174
                        poster.PostEvent(StateEvent::Failure);
×
175
                        return;
×
176
                }
177
                backoff_.Reset();
59✔
178
                ctx.inventory_client->has_submitted_inventory = true;
59✔
179
                poster.PostEvent(StateEvent::Success);
59✔
180
        };
59✔
181

182
        auto err = ctx.inventory_client->PushData(
59✔
183
                ctx.mender_context.GetConfig().paths.GetInventoryScriptsDir(),
59✔
184
                ctx.event_loop,
185
                ctx.http_client,
186
                handler);
59✔
187

188
        if (err != error::NoError) {
59✔
189
                // This is the only case the handler won't be called for us by
190
                // PushData() (see inventory::PushInventoryData()).
191
                handler(err);
×
192
        }
193
}
59✔
194

195
PollForDeploymentState::PollForDeploymentState(int retry_interval_seconds, int retry_count) :
198✔
196
        backoff_ {chrono::seconds(retry_interval_seconds), retry_count} {
99✔
197
}
198✔
198

199
void PollForDeploymentState::HandlePollingError(
18✔
200
        Context &ctx,
201
        sm::EventPoster<StateEvent> &poster,
202
        mender::update::deployments::CheckUpdatesAPIResponseError error) {
203
        // When using short polling intervals, we should adjust the backoff to ensure
204
        // that the intervals do not exceed the maximum retry polling interval, which
205
        // converts the backoff to a fixed interval.
206
        chrono::milliseconds max_interval =
207
                chrono::seconds(ctx.mender_context.GetConfig().retry_poll_interval_seconds);
18✔
208
        if (max_interval < backoff_.SmallestInterval()) {
18✔
209
                backoff_.SetSmallestInterval(max_interval);
1✔
210
                backoff_.SetMaxInterval(max_interval);
1✔
211
        }
212

213
        chrono::milliseconds interval;
214
        bool retry_after_defined {false};
215

216
        if (error.http_code.has_value() && error.http_code.value() == http::StatusTooManyRequests
14✔
217
                && error.http_headers.has_value()) {
28✔
218
                auto retry_after_header = error.http_headers.value().find("Retry-After");
10✔
219
                if (retry_after_header != error.http_headers.value().end()) {
10✔
220
                        auto exp_interval = http::GetRemainingTime(retry_after_header->second);
3✔
221
                        if (exp_interval) {
3✔
222
                                interval = exp_interval.value();
3✔
223
                                retry_after_defined = true;
224
                        } else {
UNCOV
225
                                log::Debug("Could not get the Retry-After value from HTTP response");
×
226
                        }
227
                } else {
228
                        log::Debug("Got status code TooManyRequests but no Retry-After HTTP header");
14✔
229
                }
230
        }
231

232
        if (!retry_after_defined) {
3✔
233
                auto exp_interval = backoff_.NextInterval();
15✔
234
                if (!exp_interval) {
15✔
235
                        log::Debug(
1✔
236
                                "Not retrying with backoff, retrying with UpdatePollIntervalSeconds: "
237
                                + exp_interval.error().String());
2✔
238
                        return;
239
                }
240
                interval = exp_interval.value();
14✔
241
        }
242
        log::Info(
17✔
243
                "Retrying deployment polling in "
244
                + to_string(chrono::duration_cast<chrono::seconds>(interval).count()) + " seconds");
17✔
245

246
        ctx.deployment_timer.Cancel();
17✔
247
        ctx.deployment_timer.AsyncWait(interval, [&poster](error::Error err) {
34✔
248
                if (err != error::NoError) {
3✔
UNCOV
249
                        if (err.code != make_error_condition(errc::operation_canceled)) {
×
UNCOV
250
                                log::Error("Retry poll timer caused error: " + err.String());
×
251
                        }
252
                } else {
253
                        poster.PostEvent(StateEvent::DeploymentPollingTriggered);
3✔
254
                }
255
        });
3✔
256
}
257

258
void PollForDeploymentState::CheckNewDeploymentsHandler(
72✔
259
        Context &ctx,
260
        sm::EventPoster<StateEvent> &poster,
261
        mender::update::deployments::CheckUpdatesAPIResponse response) {
262
        if (!response) {
72✔
263
                log::Error("Error while polling for deployment: " + response.error().error.String());
28✔
264
                // Replace the update poll timer with:
265
                // - a backoff, or
266
                // - if HTTP 429 Too Many Requests with  Retry-After header is provided - appropriate time
267
                // based on it
268
                HandlePollingError(ctx, poster, response.error());
14✔
269
                poster.PostEvent(StateEvent::Failure);
14✔
270
                return;
15✔
271
        } else if (!response.value()) {
58✔
272
                log::Info("No update available");
1✔
273
                poster.PostEvent(StateEvent::NothingToDo);
1✔
274
                if (not ctx.inventory_client->has_submitted_inventory) {
1✔
275
                        // If we have not submitted inventory successfully at least
276
                        // once, schedule this after receiving a successful response
277
                        // with no update. This enables inventory to be submitted
278
                        // immediately after the device has been accepted. If there
279
                        // is an update available, an inventory update will be
280
                        // scheduled at the end of it unconditionally.
UNCOV
281
                        poster.PostEvent(StateEvent::InventoryPollingTriggered);
×
282
                }
283

284
                backoff_.Reset();
285
                return;
1✔
286
        }
287
        backoff_.Reset();
288

289
        auto exp_data = ApiResponseJsonToStateData(response.value().value());
57✔
290
        if (!exp_data) {
57✔
UNCOV
291
                log::Error("Error in API response: " + exp_data.error().String());
×
UNCOV
292
                poster.PostEvent(StateEvent::Failure);
×
293
                return;
294
        }
295

296
        // Make a new set of update data.
297
        ctx.deployment.state_data.reset(new StateData(std::move(exp_data.value())));
57✔
298

299
        ctx.BeginDeploymentLogging();
57✔
300

301
        // This is a duplicate message to one logged when mender-update
302
        // starts, but this one goes into the deployment log.
303
        log::Info("Running mender-update " + conf::kMenderVersion);
114✔
304
        log::Info("Deployment with ID " + ctx.deployment.state_data->update_info.id + " started.");
57✔
305

306
        poster.PostEvent(StateEvent::DeploymentStarted);
57✔
307
        poster.PostEvent(StateEvent::Success);
57✔
308
}
309

310
void PollForDeploymentState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
62✔
311
        log::Debug("Polling for update");
124✔
312

313
        auto err = ctx.deployment_client->CheckNewDeployments(
124✔
314
                ctx.mender_context,
315
                ctx.http_client,
316
                [this, &ctx, &poster](mender::update::deployments::CheckUpdatesAPIResponse response) {
62✔
317
                        this->CheckNewDeploymentsHandler(ctx, poster, response);
58✔
318
                });
120✔
319

320
        if (err != error::NoError) {
62✔
321
                log::Error("Error when trying to poll for deployment: " + err.String());
4✔
322
                // If we're here, no handler will be called, so we need to manually schedule the next
323
                // deployment poll.
324
                // Posting Failure correctly exits the PollForDeploymentState, but does not schedule the
325
                // next poll. Thus, we need to also call HandlePollingError that adds a timer that will
326
                // cause DeploymentPollingTriggered to be posted to the state machine after a defined time.
327
                HandlePollingError(
4✔
328
                        ctx,
329
                        poster,
330
                        mender::update::deployments::CheckUpdatesAPIResponseError {nullopt, nullopt, err});
8✔
331
                poster.PostEvent(StateEvent::Failure);
4✔
332
        }
333
}
62✔
334

335
void SaveState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
535✔
336
        assert(ctx.deployment.state_data);
337

338
        ctx.deployment.state_data->state = DatabaseStateString();
535✔
339

340
        log::Trace("Storing deployment state in the DB (database-string): " + DatabaseStateString());
1,070✔
341

342
        auto err = ctx.SaveDeploymentStateData(*ctx.deployment.state_data);
535✔
343
        if (err != error::NoError) {
535✔
344
                log::Error(err.String());
10✔
345
                if (err.code
10✔
346
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
20✔
347
                        poster.PostEvent(StateEvent::StateLoopDetected);
1✔
348
                        return;
349
                } else if (!IsFailureState()) {
9✔
350
                        // Non-failure states should be interrupted, but failure states should be
351
                        // allowed to do their work, even if a database error was detected.
352
                        poster.PostEvent(StateEvent::Failure);
2✔
353
                        return;
354
                }
355
        }
356

357
        OnEnterSaveState(ctx, poster);
532✔
358
}
359

360
void UpdateDownloadState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
54✔
361
        log::Debug("Entering Download state");
54✔
362

363
        auto req = make_shared<http::OutgoingRequest>();
54✔
364
        req->SetMethod(http::Method::GET);
54✔
365
        auto err = req->SetAddress(ctx.deployment.state_data->update_info.artifact.source.uri);
54✔
366
        if (err != error::NoError) {
54✔
UNCOV
367
                log::Error(err.String());
×
UNCOV
368
                poster.PostEvent(StateEvent::Failure);
×
369
                return;
370
        }
371

372
        err = ctx.download_client->AsyncCall(
108✔
373
                req,
374
                [&ctx, &poster](http::ExpectedIncomingResponsePtr exp_resp) {
54✔
375
                        if (!exp_resp) {
54✔
376
                                log::Error("Unexpected error during download: " + exp_resp.error().String());
×
377
                                poster.PostEvent(StateEvent::Failure);
×
378
                                return;
1✔
379
                        }
380

381
                        auto &resp = exp_resp.value();
54✔
382
                        if (resp->GetStatusCode() != http::StatusOK) {
54✔
383
                                log::Error(
1✔
384
                                        "Unexpected status code while fetching artifact: " + resp->GetStatusMessage());
1✔
385
                                poster.PostEvent(StateEvent::Failure);
1✔
386
                                return;
1✔
387
                        }
388

389
                        auto http_reader = resp->MakeBodyAsyncReader();
53✔
390
                        if (!http_reader) {
53✔
UNCOV
391
                                log::Error(http_reader.error().String());
×
UNCOV
392
                                poster.PostEvent(StateEvent::Failure);
×
393
                                return;
394
                        }
395
                        ctx.deployment.artifact_reader =
53✔
396
                                make_shared<events::io::ReaderFromAsyncReader>(ctx.event_loop, http_reader.value());
53✔
397
                        ParseArtifact(ctx, poster);
53✔
398
                },
399
                [](http::ExpectedIncomingResponsePtr exp_resp) {
54✔
400
                        if (!exp_resp) {
54✔
401
                                log::Error(exp_resp.error().String());
6✔
402
                                // Cannot handle error here, because this handler is called at the
403
                                // end of the download, when we have already left this state. So
404
                                // rely on this error being propagated through the BodyAsyncReader
405
                                // above instead.
406
                                return;
6✔
407
                        }
408
                });
54✔
409

410
        if (err != error::NoError) {
54✔
UNCOV
411
                log::Error(err.String());
×
UNCOV
412
                poster.PostEvent(StateEvent::Failure);
×
413
                return;
414
        }
415
}
416

417
void UpdateDownloadState::ParseArtifact(Context &ctx, sm::EventPoster<StateEvent> &poster) {
53✔
418
        string art_scripts_path = ctx.mender_context.GetConfig().paths.GetArtScriptsPath();
53✔
419

420
        // Clear the artifact scripts directory so we don't risk old scripts lingering.
421
        auto err = path::DeleteRecursively(art_scripts_path);
53✔
422
        if (err != error::NoError) {
53✔
UNCOV
423
                log::Error("When preparing to parse artifact: " + err.String());
×
UNCOV
424
                poster.PostEvent(StateEvent::Failure);
×
425
                return;
426
        }
427

428
        artifact::config::ParserConfig config {
53✔
429
                .artifact_scripts_filesystem_path = art_scripts_path,
430
                .artifact_scripts_version = 3,
431
                .artifact_verify_keys = ctx.mender_context.GetConfig().artifact_verify_keys,
53✔
432
        };
53✔
433
        auto exp_parser = artifact::Parse(*ctx.deployment.artifact_reader, config);
53✔
434
        if (!exp_parser) {
53✔
UNCOV
435
                log::Error(exp_parser.error().String());
×
436
                poster.PostEvent(StateEvent::Failure);
×
437
                return;
438
        }
439
        ctx.deployment.artifact_parser.reset(new artifact::Artifact(std::move(exp_parser.value())));
53✔
440

441
        auto exp_header = artifact::View(*ctx.deployment.artifact_parser, 0);
53✔
442
        if (!exp_header) {
53✔
UNCOV
443
                log::Error(exp_header.error().String());
×
UNCOV
444
                poster.PostEvent(StateEvent::Failure);
×
445
                return;
446
        }
447
        auto &header = exp_header.value();
53✔
448

449
        auto exp_matches = ctx.mender_context.MatchesArtifactDepends(header.header);
53✔
450
        if (!exp_matches) {
53✔
451
                log::Error(exp_matches.error().String());
2✔
452
                poster.PostEvent(StateEvent::Failure);
2✔
453
                return;
454
        } else if (!exp_matches.value()) {
51✔
455
                // reasons already logged
456
                poster.PostEvent(StateEvent::Failure);
1✔
457
                return;
458
        }
459

460
        log::Info("Installing artifact...");
100✔
461

462
        ctx.deployment.state_data->FillUpdateDataFromArtifact(header);
50✔
463

464
        ctx.deployment.state_data->state = Context::kUpdateStateDownload;
50✔
465

466
        assert(ctx.deployment.state_data->update_info.artifact.payload_types.size() == 1);
467

468
        // Initial state data save, now that we have enough information from the artifact.
469
        err = ctx.SaveDeploymentStateData(*ctx.deployment.state_data);
50✔
470
        if (err != error::NoError) {
50✔
UNCOV
471
                log::Error(err.String());
×
UNCOV
472
                if (err.code
×
UNCOV
473
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
×
UNCOV
474
                        poster.PostEvent(StateEvent::StateLoopDetected);
×
475
                        return;
476
                } else {
477
                        poster.PostEvent(StateEvent::Failure);
×
478
                        return;
479
                }
480
        }
481

482
        if (header.header.payload_type == "") {
50✔
483
                // Empty-payload-artifact, aka "bootstrap artifact".
484
                poster.PostEvent(StateEvent::NothingToDo);
1✔
485
                return;
486
        }
487

488
        auto exp_update_module =
489
                update_module::UpdateModule::Create(ctx.mender_context, header.header.payload_type);
49✔
490
        if (!exp_update_module.has_value()) {
49✔
UNCOV
491
                log::Error(
×
492
                        "Error creating an Update Module when parsing artifact: "
493
                        + exp_update_module.error().String());
×
494
                poster.PostEvent(StateEvent::Failure);
×
495
                return;
496
        }
497
        ctx.deployment.update_module = std::move(exp_update_module.value());
49✔
498

499
        err = ctx.deployment.update_module->CleanAndPrepareFileTree(
49✔
500
                ctx.deployment.update_module->GetUpdateModuleWorkDir(), header);
49✔
501
        if (err != error::NoError) {
49✔
UNCOV
502
                log::Error(err.String());
×
UNCOV
503
                poster.PostEvent(StateEvent::Failure);
×
504
                return;
505
        }
506

507
        err = ctx.deployment.update_module->AsyncProvidePayloadFileSizes(
49✔
508
                ctx.event_loop, [&ctx, &poster](expected::ExpectedBool download_with_sizes) {
49✔
509
                        if (!download_with_sizes.has_value()) {
49✔
UNCOV
510
                                log::Error(download_with_sizes.error().String());
×
UNCOV
511
                                poster.PostEvent(StateEvent::Failure);
×
UNCOV
512
                                return;
×
513
                        }
514
                        ctx.deployment.download_with_sizes = download_with_sizes.value();
49✔
515
                        DoDownload(ctx, poster);
49✔
516
                });
49✔
517

518
        if (err != error::NoError) {
49✔
UNCOV
519
                log::Error(err.String());
×
UNCOV
520
                poster.PostEvent(StateEvent::Failure);
×
521
                return;
522
        }
523
}
53✔
524

525
void UpdateDownloadState::DoDownload(Context &ctx, sm::EventPoster<StateEvent> &poster) {
49✔
526
        auto exp_payload = ctx.deployment.artifact_parser->Next();
49✔
527
        if (!exp_payload) {
49✔
UNCOV
528
                log::Error(exp_payload.error().String());
×
UNCOV
529
                poster.PostEvent(StateEvent::Failure);
×
530
                return;
531
        }
532
        ctx.deployment.artifact_payload.reset(new artifact::Payload(std::move(exp_payload.value())));
49✔
533

534
        auto handler = [&poster, &ctx](error::Error err) {
49✔
535
                if (err != error::NoError) {
49✔
536
                        log::Error(err.String());
2✔
537
                        poster.PostEvent(StateEvent::Failure);
2✔
538
                        return;
2✔
539
                }
540

541
                auto exp_payload = ctx.deployment.artifact_parser->Next();
47✔
542
                if (exp_payload) {
47✔
UNCOV
543
                        log::Error("Multiple payloads are not yet supported in daemon mode.");
×
UNCOV
544
                        poster.PostEvent(StateEvent::Failure);
×
545
                        return;
546
                } else if (
47✔
547
                        exp_payload.error().code
548
                        != artifact::parser_error::MakeError(artifact::parser_error::EOFError, "").code) {
94✔
UNCOV
549
                        log::Error(exp_payload.error().String());
×
UNCOV
550
                        poster.PostEvent(StateEvent::Failure);
×
551
                        return;
552
                }
553

554
                poster.PostEvent(StateEvent::Success);
47✔
555
        };
556

557
        if (ctx.deployment.download_with_sizes) {
49✔
558
                ctx.deployment.update_module->AsyncDownloadWithFileSizes(
2✔
559
                        ctx.event_loop, *ctx.deployment.artifact_payload, handler);
560
        } else {
561
                ctx.deployment.update_module->AsyncDownload(
96✔
562
                        ctx.event_loop, *ctx.deployment.artifact_payload, handler);
563
        }
564
}
565

566
void UpdateDownloadCancelState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
6✔
567
        log::Debug("Entering DownloadCancel state");
12✔
568
        ctx.download_client->Cancel();
6✔
569
        poster.PostEvent(StateEvent::Success);
6✔
570
}
6✔
571

572
SendStatusUpdateState::SendStatusUpdateState(optional<deployments::DeploymentStatus> status) :
288✔
573
        status_(status),
288✔
574
        mode_(FailureMode::Ignore) {
288✔
575
}
288✔
576

577
SendStatusUpdateState::SendStatusUpdateState(
384✔
578
        optional<deployments::DeploymentStatus> status,
579
        events::EventLoop &event_loop,
580
        int retry_interval_seconds,
581
        int retry_count) :
192✔
582
        status_(status),
192✔
583
        mode_(FailureMode::RetryThenFail),
192✔
584
        retry_(Retry {
192✔
585
                http::ExponentialBackoff(chrono::seconds(retry_interval_seconds), retry_count),
586
                event_loop}) {
192✔
587
}
384✔
588

589
void SendStatusUpdateState::SetSmallestWaitInterval(chrono::milliseconds interval) {
182✔
590
        if (retry_) {
182✔
591
                retry_->backoff.SetSmallestInterval(interval);
182✔
592
        }
593
}
182✔
594

595
void SendStatusUpdateState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
246✔
596
        // Reset this every time we enter the state, which means a new round of retries.
597
        if (retry_) {
246✔
598
                retry_->backoff.Reset();
599
        }
600

601
        DoStatusUpdate(ctx, poster);
246✔
602
}
246✔
603

604
void SendStatusUpdateState::DoStatusUpdate(Context &ctx, sm::EventPoster<StateEvent> &poster) {
265✔
605
        assert(ctx.deployment_client);
606
        assert(ctx.deployment.state_data);
607

608
        log::Info("Sending status update to server");
265✔
609

610
        auto result_handler = [this, &ctx, &poster](const error::Error &err) {
265✔
611
                if (err != error::NoError) {
265✔
612
                        log::Error("Could not send deployment status: " + err.String());
25✔
613

614
                        if (err.code == deployments::MakeError(deployments::DeploymentAbortedError, "").code) {
50✔
615
                                // If the deployment was aborted upstream it is an immediate
616
                                // failure, even if retry is enabled.
617
                                poster.PostEvent(StateEvent::DeploymentAborted);
3✔
618
                                return;
3✔
619
                        }
620

621
                        switch (mode_) {
22✔
622
                        case FailureMode::Ignore:
623
                                break;
2✔
624
                        case FailureMode::RetryThenFail:
20✔
625

626
                                auto exp_interval = retry_->backoff.NextInterval();
20✔
627
                                if (!exp_interval) {
20✔
628
                                        log::Error(
1✔
629
                                                "Giving up on sending status updates to server: "
630
                                                + exp_interval.error().String());
1✔
631
                                        poster.PostEvent(StateEvent::Failure);
1✔
632
                                        return;
633
                                }
634

635
                                log::Info(
19✔
636
                                        "Retrying status update after "
637
                                        + to_string(chrono::duration_cast<chrono::seconds>(*exp_interval).count())
19✔
638
                                        + " seconds");
19✔
639
                                retry_->wait_timer.AsyncWait(
19✔
640
                                        *exp_interval, [this, &ctx, &poster](error::Error err) {
19✔
641
                                                // Error here is quite unexpected (from a timer), so treat
642
                                                // this as an immediate error, despite Retry flag.
643
                                                if (err != error::NoError) {
19✔
UNCOV
644
                                                        log::Error(
×
645
                                                                "Unexpected error in SendStatusUpdateState wait timer: "
UNCOV
646
                                                                + err.String());
×
UNCOV
647
                                                        poster.PostEvent(StateEvent::Failure);
×
UNCOV
648
                                                        return;
×
649
                                                }
650

651
                                                // Try again. Since both status and logs are sent
652
                                                // from here, there's a chance this might resubmit
653
                                                // the status, but there's no harm in it, and it
654
                                                // won't happen often.
655
                                                DoStatusUpdate(ctx, poster);
19✔
656
                                        });
657
                                return;
19✔
658
                        }
659
                }
660

661
                poster.PostEvent(StateEvent::Success);
242✔
662
        };
265✔
663

664
        deployments::DeploymentStatus status;
665
        if (status_) {
265✔
666
                status = status_.value();
172✔
667
        } else {
668
                // If nothing is specified, grab success/failure status from the deployment status.
669
                if (ctx.deployment.failed) {
93✔
670
                        status = deployments::DeploymentStatus::Failure;
671
                } else {
672
                        status = deployments::DeploymentStatus::Success;
673
                }
674
        }
675

676
        // Push status.
677
        log::Debug("Pushing deployment status: " + DeploymentStatusString(status));
530✔
678
        auto err = ctx.deployment_client->PushStatus(
265✔
679
                ctx.deployment.state_data->update_info.id,
265✔
680
                status,
681
                "",
682
                ctx.http_client,
683
                [result_handler, &ctx](error::Error err) {
265✔
684
                        // If there is an error, we don't submit logs now, but call the handler,
685
                        // which may schedule a retry later. If there is no error, and the
686
                        // deployment as a whole was successful, then also call the handler here,
687
                        // since we don't need to submit logs at all then.
688
                        if (err != error::NoError || !ctx.deployment.failed) {
265✔
689
                                result_handler(err);
190✔
690
                                return;
190✔
691
                        }
692

693
                        // Push logs.
694
                        err = ctx.deployment_client->PushLogs(
75✔
695
                                ctx.deployment.state_data->update_info.id,
75✔
696
                                ctx.deployment.logger->LogFilePath(),
150✔
697
                                ctx.http_client,
75✔
698
                                result_handler);
150✔
699

700
                        if (err != error::NoError) {
75✔
UNCOV
701
                                result_handler(err);
×
702
                        }
703
                });
265✔
704

705
        if (err != error::NoError) {
265✔
UNCOV
706
                result_handler(err);
×
707
        }
708

709
        // No action, wait for reply from status endpoint.
710
}
265✔
711

712
void UpdateInstallState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
42✔
713
        log::Debug("Entering ArtifactInstall state");
84✔
714

715
        DefaultAsyncErrorHandler(
42✔
716
                poster,
717
                ctx.deployment.update_module->AsyncArtifactInstall(
42✔
718
                        ctx.event_loop, DefaultStateHandler {poster}));
42✔
719
}
42✔
720

721
void UpdateCheckRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
73✔
722
        DefaultAsyncErrorHandler(
73✔
723
                poster,
724
                ctx.deployment.update_module->AsyncNeedsReboot(
73✔
725
                        ctx.event_loop, [&ctx, &poster](update_module::ExpectedRebootAction reboot_action) {
73✔
726
                                if (!reboot_action.has_value()) {
73✔
727
                                        log::Error(reboot_action.error().String());
2✔
728
                                        poster.PostEvent(StateEvent::Failure);
2✔
729
                                        return;
2✔
730
                                }
731

732
                                ctx.deployment.state_data->update_info.reboot_requested.resize(1);
71✔
733
                                ctx.deployment.state_data->update_info.reboot_requested[0] =
71✔
734
                                        NeedsRebootToDbString(*reboot_action);
71✔
735
                                switch (*reboot_action) {
71✔
736
                                case update_module::RebootAction::No:
8✔
737
                                        poster.PostEvent(StateEvent::NothingToDo);
8✔
738
                                        break;
8✔
739
                                case update_module::RebootAction::Yes:
63✔
740
                                case update_module::RebootAction::Automatic:
741
                                        poster.PostEvent(StateEvent::Success);
63✔
742
                                        break;
63✔
743
                                }
744
                        }));
745
}
73✔
746

747
void UpdateRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
26✔
748
        log::Debug("Entering ArtifactReboot state");
52✔
749

750
        assert(ctx.deployment.state_data->update_info.reboot_requested.size() == 1);
751
        auto exp_reboot_mode =
752
                DbStringToNeedsReboot(ctx.deployment.state_data->update_info.reboot_requested[0]);
26✔
753
        // Should always be true because we check it at load time.
754
        assert(exp_reboot_mode);
755

756
        switch (exp_reboot_mode.value()) {
26✔
UNCOV
757
        case update_module::RebootAction::No:
×
758
                // Should not happen because then we don't enter this state.
759
                assert(false);
UNCOV
760
                poster.PostEvent(StateEvent::Failure);
×
761
                break;
762
        case update_module::RebootAction::Yes:
26✔
763
                DefaultAsyncErrorHandler(
26✔
764
                        poster,
765
                        ctx.deployment.update_module->AsyncArtifactReboot(
26✔
766
                                ctx.event_loop, DefaultStateHandler {poster}));
26✔
767
                break;
26✔
UNCOV
768
        case update_module::RebootAction::Automatic:
×
769
                DefaultAsyncErrorHandler(
×
770
                        poster,
UNCOV
771
                        ctx.deployment.update_module->AsyncSystemReboot(
×
UNCOV
772
                                ctx.event_loop, DefaultStateHandler {poster}));
×
UNCOV
773
                break;
×
774
        }
775
}
26✔
776

777
void UpdateVerifyRebootState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
29✔
778
        log::Debug("Entering ArtifactVerifyReboot state");
58✔
779

780
        ctx.deployment.update_module->EnsureRootfsImageFileTree(
29✔
781
                ctx.deployment.update_module->GetUpdateModuleWorkDir());
29✔
782

783
        DefaultAsyncErrorHandler(
29✔
784
                poster,
785
                ctx.deployment.update_module->AsyncArtifactVerifyReboot(
29✔
786
                        ctx.event_loop, DefaultStateHandler {poster}));
29✔
787
}
29✔
788

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

794
        poster.PostEvent(StateEvent::Success);
22✔
795
}
22✔
796

797
void UpdateCommitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
19✔
798
        log::Debug("Entering ArtifactCommit state");
19✔
799

800
        // Explicitly check if state scripts version is supported
801
        auto err = script_executor::CheckScriptsCompatibility(
802
                ctx.mender_context.GetConfig().paths.GetRootfsScriptsPath());
19✔
803
        if (err != error::NoError) {
19✔
UNCOV
804
                log::Error("Failed script compatibility check: " + err.String());
×
UNCOV
805
                poster.PostEvent(StateEvent::Failure);
×
806
                return;
807
        }
808

809
        DefaultAsyncErrorHandler(
19✔
810
                poster,
811
                ctx.deployment.update_module->AsyncArtifactCommit(
19✔
812
                        ctx.event_loop, DefaultStateHandler {poster}));
38✔
813
}
814

815
void UpdateAfterCommitState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
19✔
816
        // Now we have committed. If we had a schema update, re-save state data with the new schema.
817
        assert(ctx.deployment.state_data);
818
        auto &state_data = *ctx.deployment.state_data;
819
        if (state_data.update_info.has_db_schema_update) {
19✔
UNCOV
820
                state_data.update_info.has_db_schema_update = false;
×
UNCOV
821
                auto err = ctx.SaveDeploymentStateData(state_data);
×
UNCOV
822
                if (err != error::NoError) {
×
UNCOV
823
                        log::Error("Not able to commit schema update: " + err.String());
×
UNCOV
824
                        poster.PostEvent(StateEvent::Failure);
×
825
                        return;
826
                }
827
        }
828

829
        poster.PostEvent(StateEvent::Success);
19✔
830
}
831

832
void UpdateCheckRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
45✔
833
        DefaultAsyncErrorHandler(
45✔
834
                poster,
835
                ctx.deployment.update_module->AsyncSupportsRollback(
45✔
836
                        ctx.event_loop, [&ctx, &poster](expected::ExpectedBool rollback_supported) {
45✔
837
                                if (!rollback_supported.has_value()) {
45✔
838
                                        log::Error(rollback_supported.error().String());
1✔
839
                                        poster.PostEvent(StateEvent::Failure);
1✔
840
                                        return;
1✔
841
                                }
842

843
                                ctx.deployment.state_data->update_info.supports_rollback =
44✔
844
                                        SupportsRollbackToDbString(*rollback_supported);
44✔
845
                                if (*rollback_supported) {
44✔
846
                                        poster.PostEvent(StateEvent::RollbackStarted);
38✔
847
                                        poster.PostEvent(StateEvent::Success);
38✔
848
                                } else {
849
                                        poster.PostEvent(StateEvent::NothingToDo);
6✔
850
                                }
851
                        }));
852
}
45✔
853

854
void UpdateRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
41✔
855
        log::Debug("Entering ArtifactRollback state");
82✔
856

857
        DefaultAsyncErrorHandler(
41✔
858
                poster,
859
                ctx.deployment.update_module->AsyncArtifactRollback(
41✔
860
                        ctx.event_loop, DefaultStateHandler {poster}));
41✔
861
}
41✔
862

863
void UpdateRollbackRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
57✔
864
        log::Debug("Entering ArtifactRollbackReboot state");
114✔
865

866
        auto exp_reboot_mode =
867
                DbStringToNeedsReboot(ctx.deployment.state_data->update_info.reboot_requested[0]);
57✔
868
        // Should always be true because we check it at load time.
869
        assert(exp_reboot_mode);
870

871
        // We ignore errors in this state as long as the ArtifactVerifyRollbackReboot state
872
        // succeeds.
873
        auto handler = [&poster](error::Error err) {
57✔
874
                if (err != error::NoError) {
57✔
875
                        log::Error(err.String());
2✔
876
                }
877
                poster.PostEvent(StateEvent::Success);
57✔
878
        };
57✔
879

880
        error::Error err;
57✔
881
        switch (exp_reboot_mode.value()) {
57✔
882
        case update_module::RebootAction::No:
883
                // Should not happen because then we don't enter this state.
884
                assert(false);
885

886
                err = error::MakeError(
×
887
                        error::ProgrammingError, "Entered UpdateRollbackRebootState with RebootAction = No");
×
UNCOV
888
                break;
×
889

890
        case update_module::RebootAction::Yes:
57✔
891
                err = ctx.deployment.update_module->AsyncArtifactRollbackReboot(ctx.event_loop, handler);
57✔
892
                break;
57✔
893

UNCOV
894
        case update_module::RebootAction::Automatic:
×
UNCOV
895
                err = ctx.deployment.update_module->AsyncSystemReboot(ctx.event_loop, handler);
×
UNCOV
896
                break;
×
897
        }
898

899
        if (err != error::NoError) {
57✔
UNCOV
900
                log::Error(err.String());
×
UNCOV
901
                poster.PostEvent(StateEvent::Success);
×
902
        }
903
}
57✔
904

905
void UpdateVerifyRollbackRebootState::OnEnterSaveState(
60✔
906
        Context &ctx, sm::EventPoster<StateEvent> &poster) {
907
        log::Debug("Entering ArtifactVerifyRollbackReboot state");
120✔
908

909
        // In this state we only retry, we don't fail. If this keeps on going forever, then the
910
        // state loop detection will eventually kick in.
911
        auto err = ctx.deployment.update_module->AsyncArtifactVerifyRollbackReboot(
120✔
912
                ctx.event_loop, [&poster](error::Error err) {
60✔
913
                        if (err != error::NoError) {
60✔
914
                                log::Error(err.String());
22✔
915
                                poster.PostEvent(StateEvent::Retry);
22✔
916
                                return;
22✔
917
                        }
918
                        poster.PostEvent(StateEvent::Success);
38✔
919
                });
60✔
920
        if (err != error::NoError) {
60✔
UNCOV
921
                log::Error(err.String());
×
UNCOV
922
                poster.PostEvent(StateEvent::Retry);
×
923
        }
924
}
60✔
925

926
void UpdateRollbackSuccessfulState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
50✔
927
        ctx.deployment.state_data->update_info.all_rollbacks_successful = true;
50✔
928
        poster.PostEvent(StateEvent::Success);
50✔
929
}
50✔
930

931
void UpdateFailureState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
55✔
932
        log::Debug("Entering ArtifactFailure state");
110✔
933

934
        DefaultAsyncErrorHandler(
55✔
935
                poster,
936
                ctx.deployment.update_module->AsyncArtifactFailure(
55✔
937
                        ctx.event_loop, DefaultStateHandler {poster}));
55✔
938
}
55✔
939

940
static string AddInconsistentSuffix(const string &str) {
21✔
941
        const auto &suffix = main_context::MenderContext::broken_artifact_name_suffix;
942
        // `string::ends_with` is C++20... grumble
943
        string ret {str};
21✔
944
        if (!common::EndsWith(ret, suffix)) {
21✔
945
                ret.append(suffix);
21✔
946
        }
947
        return ret;
21✔
948
}
949

950
void UpdateSaveProvidesState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
75✔
951
        if (ctx.deployment.failed && !ctx.deployment.rollback_failed) {
75✔
952
                // If the update failed, but we rolled back successfully, then we don't need to do
953
                // anything, just keep the old data.
954
                poster.PostEvent(StateEvent::Success);
38✔
955
                return;
38✔
956
        }
957

958
        assert(ctx.deployment.state_data);
959
        // This state should never happen: rollback failed, but update not failed??
960
        assert(!(!ctx.deployment.failed && ctx.deployment.rollback_failed));
961

962
        // We expect Cleanup to be the next state after this.
963
        ctx.deployment.state_data->state = ctx.kUpdateStateCleanup;
37✔
964

965
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
966

967
        string artifact_name;
968
        if (ctx.deployment.rollback_failed) {
37✔
969
                artifact_name = AddInconsistentSuffix(artifact.artifact_name);
38✔
970
        } else {
971
                artifact_name = artifact.artifact_name;
18✔
972
        }
973

974
        bool deploy_failed = ctx.deployment.failed;
37✔
975

976
        // Only the artifact_name and group should be committed in the case of a
977
        // failing update in order to make this consistent with the old client
978
        // behaviour.
979
        auto err = ctx.mender_context.CommitArtifactData(
74✔
980
                artifact_name,
981
                artifact.artifact_group,
37✔
982
                deploy_failed ? nullopt : optional<context::ProvidesData>(artifact.type_info_provides),
74✔
983
                /* Special case: Keep existing provides */
984
                deploy_failed ? context::ClearsProvidesData {}
74✔
985
                                          : optional<context::ClearsProvidesData>(artifact.clears_artifact_provides),
18✔
986
                [&ctx](kv_db::Transaction &txn) {
37✔
987
                        // Save the Cleanup state together with the artifact data, atomically.
988
                        return ctx.SaveDeploymentStateData(txn, *ctx.deployment.state_data);
37✔
989
                });
37✔
990
        if (err != error::NoError) {
37✔
UNCOV
991
                log::Error("Error saving artifact data: " + err.String());
×
UNCOV
992
                if (err.code
×
UNCOV
993
                        == main_context::MakeError(main_context::StateDataStoreCountExceededError, "").code) {
×
UNCOV
994
                        poster.PostEvent(StateEvent::StateLoopDetected);
×
995
                        return;
996
                }
UNCOV
997
                poster.PostEvent(StateEvent::Failure);
×
998
                return;
999
        }
1000

1001
        poster.PostEvent(StateEvent::Success);
37✔
1002
}
1003

1004
void UpdateCleanupState::OnEnterSaveState(Context &ctx, sm::EventPoster<StateEvent> &poster) {
91✔
1005
        log::Debug("Entering ArtifactCleanup state");
182✔
1006

1007
        // It's possible for there not to be an initialized update_module structure, if the
1008
        // deployment failed before we could successfully parse the artifact. If so, cleanup is a
1009
        // no-op.
1010
        if (!ctx.deployment.update_module) {
91✔
1011
                poster.PostEvent(StateEvent::Success);
9✔
1012
                return;
9✔
1013
        }
1014

1015
        DefaultAsyncErrorHandler(
82✔
1016
                poster,
1017
                ctx.deployment.update_module->AsyncCleanup(ctx.event_loop, DefaultStateHandler {poster}));
164✔
1018
}
1019

1020
void ClearArtifactDataState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
93✔
1021
        auto err = ctx.mender_context.GetMenderStoreDB().WriteTransaction([](kv_db::Transaction &txn) {
93✔
1022
                // Remove state data, since we're done now.
1023
                auto err = txn.Remove(main_context::MenderContext::state_data_key);
91✔
1024
                if (err != error::NoError) {
91✔
UNCOV
1025
                        return err;
×
1026
                }
1027
                return txn.Remove(main_context::MenderContext::state_data_key_uncommitted);
91✔
1028
        });
93✔
1029
        if (err != error::NoError) {
93✔
1030
                log::Error("Error removing artifact data: " + err.String());
2✔
1031
                poster.PostEvent(StateEvent::Failure);
2✔
1032
                return;
1033
        }
1034

1035
        poster.PostEvent(StateEvent::Success);
91✔
1036
}
1037

1038
void StateLoopState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
2✔
1039
        assert(ctx.deployment.state_data);
1040
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
1041

1042
        // Mark update as inconsistent.
1043
        string artifact_name = AddInconsistentSuffix(artifact.artifact_name);
2✔
1044

1045
        auto err = ctx.mender_context.CommitArtifactData(
6✔
1046
                artifact_name,
1047
                artifact.artifact_group,
2✔
1048
                artifact.type_info_provides,
2✔
1049
                artifact.clears_artifact_provides,
2✔
1050
                [](kv_db::Transaction &txn) { return error::NoError; });
4✔
1051
        if (err != error::NoError) {
2✔
UNCOV
1052
                log::Error("Error saving inconsistent artifact data: " + err.String());
×
UNCOV
1053
                poster.PostEvent(StateEvent::Failure);
×
1054
                return;
1055
        }
1056

1057
        poster.PostEvent(StateEvent::Success);
2✔
1058
}
1059

1060
void EndOfDeploymentState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
93✔
1061
        log::Info(
93✔
1062
                "Deployment with ID " + ctx.deployment.state_data->update_info.id
93✔
1063
                + " finished with status: " + string(ctx.deployment.failed ? "Failure" : "Success"));
297✔
1064

1065
        ctx.FinishDeploymentLogging();
93✔
1066

1067
        ctx.deployment = {};
93✔
1068
        poster.PostEvent(
93✔
1069
                StateEvent::InventoryPollingTriggered); // Submit the inventory right after an update
1070
        poster.PostEvent(StateEvent::DeploymentEnded);
93✔
1071
        poster.PostEvent(StateEvent::Success);
93✔
1072
}
93✔
1073

1074
ExitState::ExitState(events::EventLoop &event_loop) :
96✔
1075
        event_loop_(event_loop) {
96✔
1076
}
96✔
1077

1078
void ExitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
93✔
1079
#ifndef NDEBUG
1080
        if (--iterations_left_ <= 0) {
1081
                event_loop_.Stop();
1082
        } else {
1083
                poster.PostEvent(StateEvent::Success);
1084
        }
1085
#else
1086
        event_loop_.Stop();
93✔
1087
#endif
1088
}
93✔
1089

1090
namespace deployment_tracking {
1091

1092
void NoFailuresState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
63✔
1093
        ctx.deployment.failed = false;
63✔
1094
        ctx.deployment.rollback_failed = false;
63✔
1095
}
63✔
1096

1097
void FailureState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
60✔
1098
        ctx.deployment.failed = true;
60✔
1099
        ctx.deployment.rollback_failed = true;
60✔
1100
}
60✔
1101

1102
void RollbackAttemptedState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
52✔
1103
        ctx.deployment.failed = true;
52✔
1104
        ctx.deployment.rollback_failed = false;
52✔
1105
}
52✔
1106

1107
void RollbackFailedState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
12✔
1108
        ctx.deployment.failed = true;
12✔
1109
        ctx.deployment.rollback_failed = true;
12✔
1110
}
12✔
1111

1112
} // namespace deployment_tracking
1113

1114
} // namespace daemon
1115
} // namespace update
1116
} // 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

© 2026 Coveralls, Inc