• 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

76.13
/mender-update/update_module/v3/update_module.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/update_module/v3/update_module.hpp>
16

17
#include <common/events.hpp>
18
#include <common/error.hpp>
19
#include <common/expected.hpp>
20
#include <common/path.hpp>
21

22
namespace mender {
23
namespace update {
24
namespace update_module {
25
namespace v3 {
26

27
namespace error = mender::common::error;
28
namespace expected = mender::common::expected;
29
namespace path = mender::common::path;
30

31
static std::string StateString[] = {
32
        "Download",
33
        "ArtifactInstall",
34
        "NeedsArtifactReboot",
35
        "ArtifactReboot",
36
        "ArtifactCommit",
37
        "SupportsRollback",
38
        "ArtifactRollback",
39
        "ArtifactVerifyReboot",
40
        "ArtifactRollbackReboot",
41
        "ArtifactVerifyRollbackReboot",
42
        "ArtifactFailure",
43
        "Cleanup"};
44

45
std::string StateToString(State state) {
487✔
46
        static_assert(
47
                sizeof(StateString) / sizeof(*StateString) == static_cast<int>(State::LastState),
48
                "Make sure to keep State and StateString in sync!");
49
        return StateString[static_cast<int>(state)];
487✔
50
}
51

52
UpdateModule::UpdateModule(MenderContext &ctx, const string &payload_type) :
×
53
        ctx_(ctx) {
×
54
        update_module_path_ = path::Join(ctx.modules_path, payload_type);
×
55
        update_module_workdir_ =
56
                path::Join(ctx.modules_work_path, "modules", "v3", "payloads", "0000", "tree");
×
57
}
×
58

59
void UpdateModule::Cancel() {
×
60
        download_.reset();
×
61
        state_runner_.reset();
×
62
}
×
63

64
UpdateModule::DownloadData::DownloadData(
41✔
65
        events::EventLoop &event_loop, artifact::Payload &payload) :
41✔
66
        payload_(payload),
67
        event_loop_(event_loop) {
41✔
68
        buffer_.resize(MENDER_BUFSIZE);
41✔
69
}
41✔
70

71
error::Error UpdateModule::Download(artifact::Payload &payload) {
41✔
72
        events::EventLoop event_loop;
82✔
73
        error::Error err;
41✔
74
        AsyncDownload(event_loop, payload, [&event_loop, &err](error::Error inner_err) {
41✔
75
                err = inner_err;
41✔
76
                event_loop.Stop();
41✔
77
        });
82✔
78
        event_loop.Run();
41✔
79
        return err;
82✔
80
}
81

82
void UpdateModule::AsyncDownload(
41✔
83
        events::EventLoop &event_loop,
84
        artifact::Payload &payload,
85
        UpdateModule::StateFinishedHandler handler) {
86
        download_ = make_unique<DownloadData>(event_loop, payload);
41✔
87

88
        download_->download_finished_handler_ = [this, handler](error::Error err) {
41✔
89
                handler(err);
41✔
90
                download_.reset();
41✔
91
        };
123✔
92

93
        download_->event_loop_.Post([this]() { StartDownloadProcess(); });
82✔
94
}
41✔
95

96
error::Error UpdateModule::ArtifactInstall() {
29✔
97
        return CallStateNoCapture(State::ArtifactInstall);
29✔
98
}
99

100
error::Error UpdateModule::AsyncArtifactInstall(
×
101
        events::EventLoop &event_loop, StateFinishedHandler handler) {
102
        return AsyncCallStateNoCapture(event_loop, State::ArtifactInstall, handler);
×
103
}
104

105
static ExpectedRebootAction HandleNeedsRebootOutput(const expected::ExpectedString &exp_output) {
29✔
106
        if (!exp_output) {
29✔
107
                return expected::unexpected(error::Error(exp_output.error()));
2✔
108
        }
109
        auto &processStdOut = exp_output.value();
28✔
110
        if (processStdOut == "Yes") {
28✔
111
                return RebootAction::Yes;
1✔
112
        } else if (processStdOut == "No" || processStdOut == "") {
27✔
113
                return RebootAction::No;
23✔
114
        } else if (processStdOut == "Automatic") {
4✔
115
                return RebootAction::Automatic;
3✔
116
        }
117
        return expected::unexpected(error::Error(
1✔
118
                make_error_condition(errc::protocol_error),
1✔
119
                "Unexpected output from the process for NeedsReboot state"));
3✔
120
}
121

122
ExpectedRebootAction UpdateModule::NeedsReboot() {
29✔
123
        return HandleNeedsRebootOutput(CallStateCapture(State::NeedsReboot));
58✔
124
}
125

126
error::Error UpdateModule::AsyncNeedsReboot(
×
127
        events::EventLoop &event_loop, NeedsRebootFinishedHandler handler) {
128
        return AsyncCallStateCapture(
129
                event_loop, State::NeedsReboot, [handler](expected::ExpectedString exp_output) {
×
130
                        handler(HandleNeedsRebootOutput(exp_output));
×
131
                });
×
132
}
133

134
error::Error UpdateModule::ArtifactReboot() {
1✔
135
        return CallStateNoCapture(State::ArtifactReboot);
1✔
136
}
137

138
error::Error UpdateModule::AsyncArtifactReboot(
×
139
        events::EventLoop &event_loop, StateFinishedHandler handler) {
140
        return AsyncCallStateNoCapture(event_loop, State::ArtifactReboot, handler);
×
141
}
142

143
error::Error UpdateModule::ArtifactCommit() {
24✔
144
        return CallStateNoCapture(State::ArtifactCommit);
24✔
145
}
146

147
error::Error UpdateModule::AsyncArtifactCommit(
×
148
        events::EventLoop &event_loop, StateFinishedHandler handler) {
149
        return AsyncCallStateNoCapture(event_loop, State::ArtifactCommit, handler);
×
150
}
151

152
static expected::ExpectedBool HandleSupportsRollbackOutput(
36✔
153
        const expected::ExpectedString &exp_output) {
154
        if (!exp_output) {
36✔
155
                return expected::unexpected(error::Error(exp_output.error()));
2✔
156
        }
157
        auto &processStdOut = exp_output.value();
35✔
158
        if (processStdOut == "Yes") {
35✔
159
                return true;
15✔
160
        } else if (processStdOut == "No" || processStdOut == "") {
20✔
161
                return false;
19✔
162
        }
163
        return expected::unexpected(error::Error(
1✔
164
                make_error_condition(errc::protocol_error),
1✔
165
                "Unexpected output from the process for SupportsRollback state"));
3✔
166
}
167

168
expected::ExpectedBool UpdateModule::SupportsRollback() {
36✔
169
        return HandleSupportsRollbackOutput(CallStateCapture(State::SupportsRollback));
72✔
170
}
171

172
error::Error UpdateModule::AsyncSupportsRollback(
×
173
        events::EventLoop &event_loop, SupportsRollbackFinishedHandler handler) {
174
        return AsyncCallStateCapture(
175
                event_loop, State::SupportsRollback, [handler](expected::ExpectedString exp_output) {
×
176
                        handler(HandleSupportsRollbackOutput(exp_output));
×
177
                });
×
178
}
179

180
error::Error UpdateModule::ArtifactRollback() {
7✔
181
        return CallStateNoCapture(State::ArtifactRollback);
7✔
182
}
183

184
error::Error UpdateModule::AsyncArtifactRollback(
×
185
        events::EventLoop &event_loop, StateFinishedHandler handler) {
186
        return AsyncCallStateNoCapture(event_loop, State::ArtifactRollback, handler);
×
187
}
188

189
error::Error UpdateModule::ArtifactVerifyReboot() {
1✔
190
        return CallStateNoCapture(State::ArtifactVerifyReboot);
1✔
191
}
192

193
error::Error UpdateModule::AsyncArtifactVerifyReboot(
×
194
        events::EventLoop &event_loop, StateFinishedHandler handler) {
195
        return AsyncCallStateNoCapture(event_loop, State::ArtifactVerifyReboot, handler);
×
196
}
197

198
error::Error UpdateModule::ArtifactRollbackReboot() {
1✔
199
        return CallStateNoCapture(State::ArtifactRollbackReboot);
1✔
200
}
201

202
error::Error UpdateModule::AsyncArtifactRollbackReboot(
×
203
        events::EventLoop &event_loop, StateFinishedHandler handler) {
204
        return AsyncCallStateNoCapture(event_loop, State::ArtifactRollbackReboot, handler);
×
205
}
206

207
error::Error UpdateModule::ArtifactVerifyRollbackReboot() {
1✔
208
        return CallStateNoCapture(State::ArtifactVerifyRollbackReboot);
1✔
209
}
210

211
error::Error UpdateModule::AsyncArtifactVerifyRollbackReboot(
×
212
        events::EventLoop &event_loop, StateFinishedHandler handler) {
213
        return AsyncCallStateNoCapture(event_loop, State::ArtifactVerifyRollbackReboot, handler);
×
214
}
215

216
error::Error UpdateModule::ArtifactFailure() {
5✔
217
        return CallStateNoCapture(State::ArtifactFailure);
5✔
218
}
219

220
error::Error UpdateModule::AsyncArtifactFailure(
×
221
        events::EventLoop &event_loop, StateFinishedHandler handler) {
222
        return AsyncCallStateNoCapture(event_loop, State::ArtifactFailure, handler);
×
223
}
224

225
error::Error UpdateModule::Cleanup() {
28✔
226
        return CallStateNoCapture(State::Cleanup);
28✔
227
}
228

229
error::Error UpdateModule::AsyncCleanup(
×
230
        events::EventLoop &event_loop, StateFinishedHandler handler) {
231
        return AsyncCallStateNoCapture(event_loop, State::Cleanup, handler);
×
232
}
233

234
string UpdateModule::GetModulePath() const {
162✔
235
        return update_module_path_;
162✔
236
}
237

238
string UpdateModule::GetModulesWorkPath() const {
162✔
239
        return update_module_workdir_;
162✔
240
}
241

242
error::Error UpdateModule::GetProcessError(const error::Error &err) {
3✔
243
        if (err.code == make_error_condition(errc::no_such_file_or_directory)) {
3✔
244
                return context::MakeError(context::NoSuchUpdateModuleError, err.message);
×
245
        }
246
        return err;
3✔
247
}
248

249
error::Error UpdateModule::AsyncCallStateCapture(
65✔
250
        events::EventLoop &loop, State state, function<void(expected::ExpectedString)> handler) {
251
        state_runner_.reset(new StateRunner(loop, state, GetModulePath(), GetModulesWorkPath()));
65✔
252

253
        return state_runner_->AsyncCallState(
254
                state,
255
                true,
256
                chrono::seconds(ctx_.GetConfig().module_timeout_seconds),
65✔
257
                [handler](expected::expected<optional::optional<string>, error::Error> exp_output) {
65✔
258
                        if (!exp_output) {
65✔
259
                                handler(expected::unexpected(exp_output.error()));
2✔
260
                        } else {
261
                                assert(exp_output.value());
63✔
262
                                handler(exp_output.value().value());
63✔
263
                        }
264
                });
195✔
265
}
266

267
expected::ExpectedString UpdateModule::CallStateCapture(State state) {
65✔
268
        events::EventLoop loop;
130✔
269
        expected::ExpectedString ret;
130✔
270
        auto err = AsyncCallStateCapture(loop, state, [&ret, &loop](expected::ExpectedString str) {
130✔
271
                ret = str;
65✔
272
                loop.Stop();
65✔
273
        });
130✔
274

275
        if (err != error::NoError) {
65✔
276
                return expected::unexpected(err);
×
277
        }
278

279
        loop.Run();
65✔
280

281
        state_runner_.reset();
65✔
282

283
        return ret;
65✔
284
}
285

286
error::Error UpdateModule::AsyncCallStateNoCapture(
97✔
287
        events::EventLoop &loop, State state, function<void(error::Error)> handler) {
288
        state_runner_.reset(new StateRunner(loop, state, GetModulePath(), GetModulesWorkPath()));
97✔
289

290
        return state_runner_->AsyncCallState(
291
                state,
292
                false,
293
                chrono::seconds(ctx_.GetConfig().module_timeout_seconds),
97✔
294
                [handler](expected::expected<optional::optional<string>, error::Error> exp_output) {
96✔
295
                        if (!exp_output) {
96✔
296
                                handler(exp_output.error());
11✔
297
                        } else {
298
                                assert(!exp_output.value());
85✔
299
                                handler(error::NoError);
85✔
300
                        }
301
                });
290✔
302
}
303

304
error::Error UpdateModule::CallStateNoCapture(State state) {
97✔
305
        events::EventLoop loop;
194✔
306
        error::Error err;
97✔
307
        err = AsyncCallStateNoCapture(loop, state, [&err, &loop](error::Error inner_err) {
97✔
308
                err = inner_err;
96✔
309
                loop.Stop();
96✔
310
        });
194✔
311

312
        if (err != error::NoError) {
97✔
313
                return err;
1✔
314
        }
315

316
        loop.Run();
96✔
317

318
        state_runner_.reset();
96✔
319

320
        return err;
96✔
321
}
322

323
} // namespace v3
324
} // namespace update_module
325
} // namespace update
326
} // 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