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

mendersoftware / mender / 951586380

pending completion
951586380

push

gitlab-ci

kacf
chore: Use system reboot command when `Automatic` reboot is requested.

I won't add tests for this, because it is much better to test this
properly later when we get our acceptance tests running. It will be
untested till then...

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

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

4268 of 6026 relevant lines covered (70.83%)

147.81 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
        assert(ctx.deployment.state_data->update_info.reboot_requested.size() == 1);
×
278
        auto exp_reboot_mode =
279
                DbStringToNeedsReboot(ctx.deployment.state_data->update_info.reboot_requested[0]);
×
280
        // Should always be true because we check it at load time.
281
        assert(exp_reboot_mode);
×
282

283
        switch (exp_reboot_mode.value()) {
×
284
        case update_module::RebootAction::No:
×
285
                // Should not happen because then we don't enter this state.
286
                assert(false);
×
287
                poster.PostEvent(StateEvent::Failure);
288
                break;
289
        case update_module::RebootAction::Yes:
×
290
                DefaultAsyncErrorHandler(
×
291
                        poster,
292
                        ctx.deployment.update_module->AsyncArtifactReboot(
×
293
                                ctx.event_loop, DefaultStateHandler {poster}));
×
294
                break;
×
295
        case update_module::RebootAction::Automatic:
×
296
                DefaultAsyncErrorHandler(
×
297
                        poster,
298
                        ctx.deployment.update_module->AsyncSystemReboot(
×
299
                                ctx.event_loop, DefaultStateHandler {poster}));
×
300
                break;
×
301
        }
302
}
×
303

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

307
        DefaultAsyncErrorHandler(
×
308
                poster,
309
                ctx.deployment.update_module->AsyncArtifactVerifyReboot(
×
310
                        ctx.event_loop, DefaultStateHandler {poster}));
×
311
}
×
312

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

316
        DefaultAsyncErrorHandler(
×
317
                poster,
318
                ctx.deployment.update_module->AsyncArtifactCommit(
×
319
                        ctx.event_loop, DefaultStateHandler {poster}));
×
320
}
×
321

322
void UpdateCheckRollbackState::OnEnter(Context &ctx, sm::EventPoster<StateEvent> &poster) {
×
323
        DefaultAsyncErrorHandler(
×
324
                poster,
325
                ctx.deployment.update_module->AsyncSupportsRollback(
×
326
                        ctx.event_loop, [&ctx, &poster](expected::ExpectedBool rollback_supported) {
×
327
                                if (!rollback_supported.has_value()) {
×
328
                                        log::Error(rollback_supported.error().String());
×
329
                                        poster.PostEvent(StateEvent::Failure);
×
330
                                        return;
×
331
                                }
332

333
                                ctx.deployment.state_data->update_info.supports_rollback =
×
334
                                        SupportsRollbackToDbString(*rollback_supported);
×
335
                                if (*rollback_supported) {
×
336
                                        poster.PostEvent(StateEvent::Success);
×
337
                                } else {
338
                                        poster.PostEvent(StateEvent::NothingToDo);
×
339
                                }
340
                        }));
×
341
}
×
342

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

346
        DefaultAsyncErrorHandler(
×
347
                poster,
348
                ctx.deployment.update_module->AsyncArtifactRollback(
×
349
                        ctx.event_loop, DefaultStateHandler {poster}));
×
350
}
×
351

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

355
        DefaultAsyncErrorHandler(
×
356
                poster,
357
                ctx.deployment.update_module->AsyncArtifactRollbackReboot(
×
358
                        ctx.event_loop, DefaultStateHandler {poster}));
×
359
}
×
360

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

364
        DefaultAsyncErrorHandler(
×
365
                poster,
366
                ctx.deployment.update_module->AsyncArtifactVerifyRollbackReboot(
×
367
                        ctx.event_loop, DefaultStateHandler {poster}));
×
368
}
×
369

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

373
        DefaultAsyncErrorHandler(
×
374
                poster,
375
                ctx.deployment.update_module->AsyncArtifactFailure(
×
376
                        ctx.event_loop, DefaultStateHandler {poster}));
×
377
}
×
378

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

382
        auto err = ctx.mender_context.CommitArtifactData(
×
383
                artifact.artifact_name,
×
384
                artifact.artifact_group,
×
385
                artifact.type_info_provides,
×
386
                artifact.clears_artifact_provides,
×
387
                [](kv_db::Transaction &txn) {
×
388
                        // TODO: Erase State Data.
389
                        return error::NoError;
×
390
                });
×
391
        if (err != error::NoError) {
×
392
                log::Error("Error saving artifact data: " + err.String());
×
393
                poster.PostEvent(StateEvent::Failure);
×
394
                return;
×
395
        }
396

397
        poster.PostEvent(StateEvent::Success);
×
398
}
399

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

403
        DefaultAsyncErrorHandler(
×
404
                poster,
405
                ctx.deployment.update_module->AsyncCleanup(
×
406
                        ctx.event_loop, [&ctx, &poster](error::Error err) {
×
407
                                DefaultStateHandler handler {poster};
×
408
                                handler(err);
×
409

410
                                ctx.deployment = {};
×
411
                        }));
×
412
}
×
413

414
} // namespace daemon
415
} // namespace update
416
} // 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