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

mendersoftware / mender / 947394036

pending completion
947394036

push

gitlab-ci

kacf
chore: Treat events with no state transitions as fatal.

This was discussed with the team members. Since an unhandled event is
almost guaranteed to hang the state machine, then it's better to
terminate and let systemd try to restart us, in the hopes that
recovery will still work.

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

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

4268 of 5997 relevant lines covered (71.17%)

148.52 hits per line

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

0.0
/mender-update/daemon/states.cpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <mender-update/daemon/states.hpp>
16

17
#include <common/conf.hpp>
18
#include <common/events_io.hpp>
19
#include <common/log.hpp>
20

21
#include <mender-update/daemon/context.hpp>
22

23
namespace mender {
24
namespace update {
25
namespace daemon {
26

27
namespace conf = mender::common::conf;
28
namespace error = mender::common::error;
29
namespace events = mender::common::events;
30
namespace log = mender::common::log;
31
namespace kv_db = mender::common::key_value_database;
32

33
class DefaultStateHandler {
34
public:
35
        void operator()(const error::Error &err) {
×
36
                if (err != error::NoError) {
×
37
                        log::Error(err.String());
×
38
                        poster.PostEvent(StateEvent::Failure);
×
39
                        return;
×
40
                }
41
                poster.PostEvent(StateEvent::Success);
×
42
        }
43

44
        sm::EventPoster<StateEvent> &poster;
45
};
46

47
static void DefaultAsyncErrorHandler(sm::EventPoster<StateEvent> &poster, const error::Error &err) {
×
48
        if (err != error::NoError) {
×
49
                poster.PostEvent(StateEvent::Failure);
×
50
        }
51
}
×
52

53
void IdleState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
54
        log::Debug("Entering Idle state");
×
55
}
×
56

57
void SubmitInventoryState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
58
        log::Debug("Submitting inventory");
×
59

60
        // Schedule timer for next update first, so that long running submissions do not postpone
61
        // the schedule.
62
        poll_timer_.AsyncWait(
×
63
                chrono::seconds(ctx.mender_context.GetConfig().inventory_poll_interval_seconds),
×
64
                [&poster](error::Error err) {
×
65
                        if (err != error::NoError) {
×
66
                                log::Error("Inventory poll timer caused error: " + err.String());
×
67
                        } else {
68
                                poster.PostEvent(StateEvent::InventoryPollingTriggered);
×
69
                        }
70
                });
×
71

72
        // TODO: MEN-6576
73
        poster.PostEvent(StateEvent::Success);
×
74
}
×
75

76
void PollForDeploymentState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
77
        log::Debug("Polling for update");
×
78

79
        // Schedule timer for next update first, so that long running submissions do not postpone
80
        // the schedule.
81
        poll_timer_.AsyncWait(
×
82
                chrono::seconds(ctx.mender_context.GetConfig().update_poll_interval_seconds),
×
83
                [&poster](error::Error err) {
×
84
                        if (err != error::NoError) {
×
85
                                log::Error("Update poll timer caused error: " + err.String());
×
86
                        } else {
87
                                poster.PostEvent(StateEvent::DeploymentPollingTriggered);
×
88
                        }
89
                });
×
90

91
        auto err = ctx.deployment_client->CheckNewDeployments(
×
92
                ctx.mender_context,
93
                ctx.mender_context.GetConfig().server_url,
×
94
                ctx.http_client,
×
95
                [&ctx, &poster](mender::update::deployments::CheckUpdatesAPIResponse response) {
×
96
                        if (!response) {
×
97
                                log::Error("Error while polling for deployment: " + response.error().String());
×
98
                                poster.PostEvent(StateEvent::Failure);
×
99
                                return;
×
100
                        } else if (!response.value()) {
×
101
                                log::Info("No update available");
×
102
                                poster.PostEvent(StateEvent::NothingToDo);
×
103
                                return;
×
104
                        }
105

106
                        auto exp_data = ApiResponseJsonToStateData(response.value().value());
×
107
                        if (!exp_data) {
×
108
                                log::Error("Error in API response: " + exp_data.error().String());
×
109
                                poster.PostEvent(StateEvent::Failure);
×
110
                                return;
×
111
                        }
112

113
                        // Make a new set of update data.
114
                        ctx.deployment.state_data.reset(new StateData(std::move(exp_data.value())));
×
115

116
                        log::Info(
×
117
                                "Deployment with ID " + ctx.deployment.state_data->update_info.id + " started.");
×
118

119
                        poster.PostEvent(StateEvent::Success);
×
120
                });
×
121

122
        if (err != error::NoError) {
×
123
                log::Error("Error when trying to poll for deployment: " + err.String());
×
124
                poster.PostEvent(StateEvent::Failure);
×
125
        }
126
}
×
127

128
void UpdateDownloadState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
129
        log::Debug("Entering Download state");
×
130

131
        auto req = make_shared<http::OutgoingRequest>();
×
132
        req->SetMethod(http::Method::GET);
×
133
        auto err = req->SetAddress(ctx.deployment.state_data->update_info.artifact.source.uri);
×
134
        if (err != error::NoError) {
×
135
                log::Error(err.String());
×
136
                poster.PostEvent(StateEvent::Failure);
×
137
                return;
×
138
        }
139

140
        err = ctx.download_client.AsyncCall(
×
141
                req,
142
                [&ctx, &poster](http::ExpectedIncomingResponsePtr exp_resp) {
×
143
                        if (!exp_resp) {
×
144
                                log::Error(exp_resp.error().String());
×
145
                                poster.PostEvent(StateEvent::Failure);
×
146
                                return;
×
147
                        }
148

149
                        auto &resp = exp_resp.value();
×
150
                        if (resp->GetStatusCode() != http::StatusOK) {
×
151
                                log::Error(
×
152
                                        "Unexpected status code while fetching artifact: " + resp->GetStatusMessage());
×
153
                                ctx.download_client.Cancel();
×
154
                                poster.PostEvent(StateEvent::Failure);
×
155
                                return;
×
156
                        }
157

158
                        auto http_reader = resp->MakeBodyAsyncReader();
×
159
                        ctx.deployment.artifact_reader =
160
                                make_shared<events::io::ReaderFromAsyncReader>(ctx.event_loop, http_reader);
×
161
                        ParseArtifact(ctx, poster);
×
162
                },
163
                [&poster](http::ExpectedIncomingResponsePtr exp_resp) {
×
164
                        if (!exp_resp) {
×
165
                                log::Error(exp_resp.error().String());
×
166
                                poster.PostEvent(StateEvent::Failure);
×
167
                                return;
×
168
                        }
169
                });
×
170

171
        if (err != error::NoError) {
×
172
                log::Error(err.String());
×
173
                poster.PostEvent(StateEvent::Failure);
×
174
                return;
×
175
        }
176
}
177

178
void UpdateDownloadState::ParseArtifact(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
179
        artifact::config::ParserConfig config {
180
                conf::paths::DefaultArtScriptsPath,
181
        };
×
182
        auto exp_parser = artifact::Parse(*ctx.deployment.artifact_reader, config);
×
183
        if (!exp_parser) {
×
184
                log::Error(exp_parser.error().String());
×
185
                poster.PostEvent(StateEvent::Failure);
×
186
                return;
×
187
        }
188
        ctx.deployment.artifact_parser.reset(new artifact::Artifact(std::move(exp_parser.value())));
×
189

190
        auto exp_header = artifact::View(*ctx.deployment.artifact_parser, 0);
×
191
        if (!exp_header) {
×
192
                log::Error(exp_header.error().String());
×
193
                poster.PostEvent(StateEvent::Failure);
×
194
                return;
×
195
        }
196
        auto &header = exp_header.value();
×
197

198
        log::Info("Installing artifact...");
×
199

200
        ctx.deployment.state_data->FillUpdateDataFromArtifact(header);
×
201

202
        if (header.header.payload_type == "") {
×
203
                // Empty-payload-artifact, aka "bootstrap artifact".
204
                poster.PostEvent(StateEvent::NothingToDo);
×
205
                return;
×
206
        }
207

208
        ctx.deployment.update_module.reset(
×
209
                new update_module::UpdateModule(ctx.mender_context, header.header.payload_type));
×
210

211
        auto err = ctx.deployment.update_module->CleanAndPrepareFileTree(
212
                ctx.deployment.update_module->GetUpdateModuleWorkDir(), header);
×
213
        if (err != error::NoError) {
×
214
                log::Error(err.String());
×
215
                poster.PostEvent(StateEvent::Failure);
×
216
                return;
×
217
        }
218

219
        auto exp_payload = ctx.deployment.artifact_parser->Next();
×
220
        if (!exp_payload) {
×
221
                log::Error(exp_payload.error().String());
×
222
                poster.PostEvent(StateEvent::Failure);
×
223
                return;
×
224
        }
225
        ctx.deployment.artifact_payload.reset(new artifact::Payload(std::move(exp_payload.value())));
×
226

227
        ctx.deployment.update_module->AsyncDownload(
×
228
                ctx.event_loop, *ctx.deployment.artifact_payload, [&poster](error::Error err) {
×
229
                        if (err != error::NoError) {
×
230
                                log::Error(err.String());
×
231
                                poster.PostEvent(StateEvent::Failure);
×
232
                                return;
×
233
                        }
234

235
                        poster.PostEvent(StateEvent::Success);
×
236
                });
×
237
}
238

239
void UpdateInstallState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
240
        log::Debug("Entering ArtifactInstall state");
×
241

242
        DefaultAsyncErrorHandler(
×
243
                poster,
244
                ctx.deployment.update_module->AsyncArtifactInstall(
×
245
                        ctx.event_loop, DefaultStateHandler {poster}));
×
246
}
×
247

248
void UpdateCheckRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
249
        DefaultAsyncErrorHandler(
×
250
                poster,
251
                ctx.deployment.update_module->AsyncNeedsReboot(
×
252
                        ctx.event_loop, [&ctx, &poster](update_module::ExpectedRebootAction reboot_action) {
×
253
                                if (!reboot_action.has_value()) {
×
254
                                        log::Error(reboot_action.error().String());
×
255
                                        poster.PostEvent(StateEvent::Failure);
×
256
                                        return;
×
257
                                }
258

259
                                ctx.deployment.state_data->update_info.reboot_requested.resize(1);
×
260
                                ctx.deployment.state_data->update_info.reboot_requested[0] =
×
261
                                        NeedsRebootToDbString(*reboot_action);
×
262
                                switch (*reboot_action) {
×
263
                                case update_module::RebootAction::No:
×
264
                                        poster.PostEvent(StateEvent::NothingToDo);
×
265
                                        break;
×
266
                                case update_module::RebootAction::Yes:
×
267
                                case update_module::RebootAction::Automatic:
268
                                        poster.PostEvent(StateEvent::Success);
×
269
                                        break;
×
270
                                }
271
                        }));
×
272
}
×
273

274
void UpdateRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
275
        log::Debug("Entering ArtifactReboot state");
×
276

277
        DefaultAsyncErrorHandler(
×
278
                poster,
279
                ctx.deployment.update_module->AsyncArtifactReboot(
×
280
                        ctx.event_loop, DefaultStateHandler {poster}));
×
281
}
×
282

283
void UpdateVerifyRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
284
        log::Debug("Entering ArtifactVerifyReboot state");
×
285

286
        DefaultAsyncErrorHandler(
×
287
                poster,
288
                ctx.deployment.update_module->AsyncArtifactVerifyReboot(
×
289
                        ctx.event_loop, DefaultStateHandler {poster}));
×
290
}
×
291

292
void UpdateCommitState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
293
        log::Debug("Entering ArtifactCommit state");
×
294

295
        DefaultAsyncErrorHandler(
×
296
                poster,
297
                ctx.deployment.update_module->AsyncArtifactCommit(
×
298
                        ctx.event_loop, DefaultStateHandler {poster}));
×
299
}
×
300

301
void UpdateCheckRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
302
        DefaultAsyncErrorHandler(
×
303
                poster,
304
                ctx.deployment.update_module->AsyncSupportsRollback(
×
305
                        ctx.event_loop, [&ctx, &poster](expected::ExpectedBool rollback_supported) {
×
306
                                if (!rollback_supported.has_value()) {
×
307
                                        log::Error(rollback_supported.error().String());
×
308
                                        poster.PostEvent(StateEvent::Failure);
×
309
                                        return;
×
310
                                }
311

312
                                ctx.deployment.state_data->update_info.supports_rollback =
×
313
                                        SupportsRollbackToDbString(*rollback_supported);
×
314
                                if (*rollback_supported) {
×
315
                                        poster.PostEvent(StateEvent::Success);
×
316
                                } else {
317
                                        poster.PostEvent(StateEvent::NothingToDo);
×
318
                                }
319
                        }));
×
320
}
×
321

322
void UpdateRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
323
        log::Debug("Entering ArtifactRollback state");
×
324

325
        DefaultAsyncErrorHandler(
×
326
                poster,
327
                ctx.deployment.update_module->AsyncArtifactRollback(
×
328
                        ctx.event_loop, DefaultStateHandler {poster}));
×
329
}
×
330

331
void UpdateRollbackRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
332
        log::Debug("Entering ArtifactRollbackReboot state");
×
333

334
        DefaultAsyncErrorHandler(
×
335
                poster,
336
                ctx.deployment.update_module->AsyncArtifactRollbackReboot(
×
337
                        ctx.event_loop, DefaultStateHandler {poster}));
×
338
}
×
339

340
void UpdateVerifyRollbackRebootState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
341
        log::Debug("Entering ArtifactVerifyRollbackReboot state");
×
342

343
        DefaultAsyncErrorHandler(
×
344
                poster,
345
                ctx.deployment.update_module->AsyncArtifactVerifyRollbackReboot(
×
346
                        ctx.event_loop, DefaultStateHandler {poster}));
×
347
}
×
348

349
void UpdateFailureState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
350
        log::Debug("Entering ArtifactFailure state");
×
351

352
        DefaultAsyncErrorHandler(
×
353
                poster,
354
                ctx.deployment.update_module->AsyncArtifactFailure(
×
355
                        ctx.event_loop, DefaultStateHandler {poster}));
×
356
}
×
357

358
void UpdateSaveArtifactDataState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
359
        auto &artifact = ctx.deployment.state_data->update_info.artifact;
×
360

361
        auto err = ctx.mender_context.CommitArtifactData(
×
362
                artifact.artifact_name,
×
363
                artifact.artifact_group,
×
364
                artifact.type_info_provides,
×
365
                artifact.clears_artifact_provides,
×
366
                [](kv_db::Transaction &txn) {
×
367
                        // TODO: Erase State Data.
368
                        return error::NoError;
×
369
                });
×
370
        if (err != error::NoError) {
×
371
                log::Error("Error saving artifact data: " + err.String());
×
372
                poster.PostEvent(StateEvent::Failure);
×
373
                return;
×
374
        }
375

376
        poster.PostEvent(StateEvent::Success);
×
377
}
378

379
void UpdateCleanupState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
380
        log::Debug("Entering ArtifactCleanup state");
×
381

382
        DefaultAsyncErrorHandler(
×
383
                poster,
384
                ctx.deployment.update_module->AsyncCleanup(
×
385
                        ctx.event_loop, [&ctx, &poster](error::Error err) {
×
386
                                DefaultStateHandler handler {poster};
×
387
                                handler(err);
×
388

389
                                ctx.deployment = {};
×
390
                        }));
×
391
}
×
392

393
} // namespace daemon
394
} // namespace update
395
} // 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