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

mendersoftware / mender / 2271300743

19 Jan 2026 11:42AM UTC coverage: 81.376% (+1.7%) from 79.701%
2271300743

push

gitlab-ci

web-flow
Merge pull request #1879 from lluiscampos/MEN-8687-ci-debian-updates

MEN-8687: Update Debian base images for CI jobs

8791 of 10803 relevant lines covered (81.38%)

20310.08 hits per line

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

96.26
/src/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
        "ProvidePayloadFileSizes",
33
        "Download",
34
        "DownloadWithFileSizes",
35
        "ArtifactInstall",
36
        "NeedsArtifactReboot",
37
        "ArtifactReboot",
38
        "ArtifactCommit",
39
        "SupportsRollback",
40
        "ArtifactRollback",
41
        "ArtifactVerifyReboot",
42
        "ArtifactRollbackReboot",
43
        "ArtifactVerifyRollbackReboot",
44
        "ArtifactFailure",
45
        "Cleanup"};
46

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

54
expected::Expected<std::unique_ptr<UpdateModule>> UpdateModule::Create(
215✔
55
        MenderContext &ctx, const string &payload_type) {
56
        auto update_module_path = path::Join(ctx.GetConfig().paths.GetModulesPath(), payload_type);
430✔
57
        auto exp_path_is_safe =
58
                path::IsWithinOrEqual(update_module_path, ctx.GetConfig().paths.GetModulesPath());
430✔
59
        if (!exp_path_is_safe.has_value()) {
215✔
60
                return expected::unexpected(exp_path_is_safe.error().WithContext(
×
61
                        "Error checking if path is equal to or within directory"));
×
62
        }
63
        if (!exp_path_is_safe.value()) {
215✔
64
                return expected::unexpected(context::MakeError(
2✔
65
                        context::NoSuchUpdateModuleError,
66
                        "Error creating Update Module: Provided Module path is outside Update Modules directory."));
1✔
67
        }
68

69
        return std::unique_ptr<UpdateModule>(new UpdateModule(ctx, payload_type, update_module_path));
428✔
70
}
71

72
UpdateModule::UpdateModule(
214✔
73
        MenderContext &ctx, const string &payload_type, string update_module_path) :
214✔
74
        ctx_ {ctx} {
214✔
75
        update_module_path_ = update_module_path;
214✔
76
        update_module_workdir_ =
77
                path::Join(ctx.GetConfig().paths.GetModulesWorkPath(), "payloads", "0000", "tree");
214✔
78
}
214✔
79

80
UpdateModule::DownloadData::DownloadData(
136✔
81
        events::EventLoop &event_loop, artifact::Payload &payload) :
136✔
82
        payload_ {payload},
136✔
83
        event_loop_ {event_loop} {
136✔
84
        buffer_.resize(MENDER_BUFSIZE);
136✔
85
}
136✔
86

87
static expected::ExpectedBool HandleProvidePayloadFileSizesOutput(
109✔
88
        const expected::ExpectedString &exp_output) {
89
        if (!exp_output) {
109✔
90
                return expected::unexpected(error::Error(exp_output.error()));
3✔
91
        }
92
        auto &processStdOut = exp_output.value();
108✔
93
        if (processStdOut == "Yes") {
108✔
94
                return true;
95
        } else if (processStdOut == "No" || processStdOut == "") {
105✔
96
                return false;
97
        }
98
        return expected::unexpected(error::Error(
1✔
99
                make_error_condition(errc::protocol_error),
2✔
100
                "Unexpected output from the process for ProvidePayloadFileSizes state: " + processStdOut));
3✔
101
}
102

103
expected::ExpectedBool UpdateModule::ProvidePayloadFileSizes() {
60✔
104
        return HandleProvidePayloadFileSizesOutput(CallStateCapture(State::ProvidePayloadFileSizes));
120✔
105
}
106

107
error::Error UpdateModule::AsyncProvidePayloadFileSizes(
49✔
108
        events::EventLoop &event_loop, ProvidePayloadFileSizesFinishedHandler handler) {
109
        return AsyncCallStateCapture(
49✔
110
                event_loop, State::ProvidePayloadFileSizes, [handler](expected::ExpectedString exp_output) {
931✔
111
                        handler(HandleProvidePayloadFileSizesOutput(exp_output));
49✔
112
                });
147✔
113
}
114

115
error::Error UpdateModule::Download(artifact::Payload &payload) {
85✔
116
        events::EventLoop event_loop;
117
        error::Error err;
85✔
118
        AsyncDownload(event_loop, payload, [&event_loop, &err](error::Error inner_err) {
85✔
119
                err = inner_err;
85✔
120
                event_loop.Stop();
85✔
121
        });
85✔
122
        event_loop.Run();
85✔
123
        return err;
85✔
124
}
125

126
void UpdateModule::AsyncDownload(
133✔
127
        events::EventLoop &event_loop,
128
        artifact::Payload &payload,
129
        UpdateModule::StateFinishedHandler handler) {
130
        download_ = make_unique<DownloadData>(event_loop, payload);
266✔
131

132
        download_->download_finished_handler_ = [this, handler](error::Error err) {
399✔
133
                handler(err);
133✔
134
                download_.reset();
133✔
135
        };
266✔
136

137
        download_->event_loop_.Post([this]() { StartDownloadProcess(); });
266✔
138
}
133✔
139

140
error::Error UpdateModule::DownloadWithFileSizes(artifact::Payload &payload) {
2✔
141
        events::EventLoop event_loop;
142
        error::Error err;
2✔
143
        AsyncDownloadWithFileSizes(event_loop, payload, [&event_loop, &err](error::Error inner_err) {
2✔
144
                err = inner_err;
2✔
145
                event_loop.Stop();
2✔
146
        });
2✔
147
        event_loop.Run();
2✔
148
        return err;
2✔
149
}
150

151
void UpdateModule::AsyncDownloadWithFileSizes(
3✔
152
        events::EventLoop &event_loop,
153
        artifact::Payload &payload,
154
        UpdateModule::StateFinishedHandler handler) {
155
        download_ = make_unique<DownloadData>(event_loop, payload);
6✔
156
        download_->downloading_with_sizes_ = true;
3✔
157

158
        download_->download_finished_handler_ = [this, handler](error::Error err) {
9✔
159
                handler(err);
3✔
160
                download_.reset();
3✔
161
        };
6✔
162

163
        download_->event_loop_.Post([this]() { StartDownloadProcess(); });
6✔
164
}
3✔
165

166
error::Error UpdateModule::ArtifactInstall() {
52✔
167
        return CallStateNoCapture(State::ArtifactInstall);
52✔
168
}
169

170
error::Error UpdateModule::AsyncArtifactInstall(
42✔
171
        events::EventLoop &event_loop, StateFinishedHandler handler) {
172
        return AsyncCallStateNoCapture(event_loop, State::ArtifactInstall, handler);
84✔
173
}
174

175
static ExpectedRebootAction HandleNeedsRebootOutput(const expected::ExpectedString &exp_output) {
122✔
176
        if (!exp_output) {
122✔
177
                return expected::unexpected(error::Error(exp_output.error()));
9✔
178
        }
179
        auto &processStdOut = exp_output.value();
119✔
180
        if (processStdOut == "Yes") {
119✔
181
                return RebootAction::Yes;
182
        } else if (processStdOut == "No" || processStdOut == "") {
55✔
183
                return RebootAction::No;
184
        } else if (processStdOut == "Automatic") {
4✔
185
                return RebootAction::Automatic;
186
        }
187
        return expected::unexpected(error::Error(
1✔
188
                make_error_condition(errc::protocol_error),
2✔
189
                "Unexpected output from the process for NeedsReboot state: " + processStdOut));
3✔
190
}
191

192
ExpectedRebootAction UpdateModule::NeedsReboot() {
49✔
193
        return HandleNeedsRebootOutput(CallStateCapture(State::NeedsReboot));
98✔
194
}
195

196
error::Error UpdateModule::AsyncNeedsReboot(
73✔
197
        events::EventLoop &event_loop, NeedsRebootFinishedHandler handler) {
198
        return AsyncCallStateCapture(
73✔
199
                event_loop, State::NeedsReboot, [handler](expected::ExpectedString exp_output) {
1,387✔
200
                        handler(HandleNeedsRebootOutput(exp_output));
73✔
201
                });
219✔
202
}
203

204
error::Error UpdateModule::ArtifactReboot() {
1✔
205
        return CallStateNoCapture(State::ArtifactReboot);
1✔
206
}
207

208
error::Error UpdateModule::AsyncArtifactReboot(
26✔
209
        events::EventLoop &event_loop, StateFinishedHandler handler) {
210
        return AsyncCallStateNoCapture(event_loop, State::ArtifactReboot, handler);
52✔
211
}
212

213
error::Error UpdateModule::ArtifactCommit() {
40✔
214
        return CallStateNoCapture(State::ArtifactCommit);
40✔
215
}
216

217
error::Error UpdateModule::AsyncArtifactCommit(
19✔
218
        events::EventLoop &event_loop, StateFinishedHandler handler) {
219
        return AsyncCallStateNoCapture(event_loop, State::ArtifactCommit, handler);
38✔
220
}
221

222
static expected::ExpectedBool HandleSupportsRollbackOutput(
112✔
223
        const expected::ExpectedString &exp_output) {
224
        if (!exp_output) {
112✔
225
                return expected::unexpected(error::Error(exp_output.error()));
6✔
226
        }
227
        auto &processStdOut = exp_output.value();
110✔
228
        if (processStdOut == "Yes") {
110✔
229
                return true;
230
        } else if (processStdOut == "No" || processStdOut == "") {
50✔
231
                return false;
232
        }
233
        return expected::unexpected(error::Error(
1✔
234
                make_error_condition(errc::protocol_error),
2✔
235
                "Unexpected output from the process for SupportsRollback state: " + processStdOut));
3✔
236
}
237

238
expected::ExpectedBool UpdateModule::SupportsRollback() {
67✔
239
        return HandleSupportsRollbackOutput(CallStateCapture(State::SupportsRollback));
134✔
240
}
241

242
error::Error UpdateModule::AsyncSupportsRollback(
45✔
243
        events::EventLoop &event_loop, SupportsRollbackFinishedHandler handler) {
244
        return AsyncCallStateCapture(
45✔
245
                event_loop, State::SupportsRollback, [handler](expected::ExpectedString exp_output) {
855✔
246
                        handler(HandleSupportsRollbackOutput(exp_output));
45✔
247
                });
135✔
248
}
249

250
error::Error UpdateModule::ArtifactRollback() {
10✔
251
        return CallStateNoCapture(State::ArtifactRollback);
10✔
252
}
253

254
error::Error UpdateModule::AsyncArtifactRollback(
41✔
255
        events::EventLoop &event_loop, StateFinishedHandler handler) {
256
        return AsyncCallStateNoCapture(event_loop, State::ArtifactRollback, handler);
82✔
257
}
258

259
error::Error UpdateModule::ArtifactVerifyReboot() {
1✔
260
        return CallStateNoCapture(State::ArtifactVerifyReboot);
1✔
261
}
262

263
error::Error UpdateModule::AsyncArtifactVerifyReboot(
29✔
264
        events::EventLoop &event_loop, StateFinishedHandler handler) {
265
        return AsyncCallStateNoCapture(event_loop, State::ArtifactVerifyReboot, handler);
58✔
266
}
267

268
error::Error UpdateModule::ArtifactRollbackReboot() {
1✔
269
        return CallStateNoCapture(State::ArtifactRollbackReboot);
1✔
270
}
271

272
error::Error UpdateModule::AsyncArtifactRollbackReboot(
57✔
273
        events::EventLoop &event_loop, StateFinishedHandler handler) {
274
        return AsyncCallStateNoCapture(event_loop, State::ArtifactRollbackReboot, handler);
114✔
275
}
276

277
error::Error UpdateModule::ArtifactVerifyRollbackReboot() {
1✔
278
        return CallStateNoCapture(State::ArtifactVerifyRollbackReboot);
1✔
279
}
280

281
error::Error UpdateModule::AsyncArtifactVerifyRollbackReboot(
60✔
282
        events::EventLoop &event_loop, StateFinishedHandler handler) {
283
        return AsyncCallStateNoCapture(event_loop, State::ArtifactVerifyRollbackReboot, handler);
120✔
284
}
285

286
error::Error UpdateModule::ArtifactFailure() {
18✔
287
        return CallStateNoCapture(State::ArtifactFailure);
18✔
288
}
289

290
error::Error UpdateModule::AsyncArtifactFailure(
55✔
291
        events::EventLoop &event_loop, StateFinishedHandler handler) {
292
        return AsyncCallStateNoCapture(event_loop, State::ArtifactFailure, handler);
110✔
293
}
294

295
error::Error UpdateModule::Cleanup() {
56✔
296
        return CallStateNoCapture(State::Cleanup);
56✔
297
}
298

299
error::Error UpdateModule::AsyncCleanup(
82✔
300
        events::EventLoop &event_loop, StateFinishedHandler handler) {
301
        return AsyncCallStateNoCapture(event_loop, State::Cleanup, handler);
164✔
302
}
303

304
string UpdateModule::GetModulePath() const {
×
305
        return update_module_path_;
934✔
306
}
307

308
string UpdateModule::GetModulesWorkPath() const {
×
309
        return update_module_workdir_;
934✔
310
}
311

312
error::Error UpdateModule::GetProcessError(const error::Error &err) {
5✔
313
        if (err.code == make_error_condition(errc::no_such_file_or_directory)) {
5✔
314
                return context::MakeError(context::NoSuchUpdateModuleError, err.message);
×
315
        }
316
        return err;
5✔
317
}
318

319
error::Error UpdateModule::AsyncCallStateCapture(
343✔
320
        events::EventLoop &loop, State state, function<void(expected::ExpectedString)> handler) {
321
        state_runner_.reset(new StateRunner(loop, state, GetModulePath(), GetModulesWorkPath()));
1,029✔
322

323
        return state_runner_->AsyncCallState(
343✔
324
                state,
325
                true,
326
                chrono::seconds(ctx_.GetConfig().module_timeout_seconds),
343✔
327
                [handler](expected::expected<optional<string>, error::Error> exp_output) {
6,174✔
328
                        if (!exp_output) {
343✔
329
                                handler(expected::unexpected(exp_output.error()));
12✔
330
                        } else {
331
                                assert(exp_output.value());
332
                                handler(exp_output.value().value());
674✔
333
                        }
334
                });
1,029✔
335
}
336

337
expected::ExpectedString UpdateModule::CallStateCapture(State state) {
176✔
338
        events::EventLoop loop;
339
        expected::ExpectedString ret;
340
        auto err = AsyncCallStateCapture(loop, state, [&ret, &loop](expected::ExpectedString str) {
×
341
                ret = str;
176✔
342
                loop.Stop();
176✔
343
        });
176✔
344

345
        if (err != error::NoError) {
176✔
346
                return expected::unexpected(err);
×
347
        }
348

349
        loop.Run();
176✔
350

351
        state_runner_.reset();
352

353
        return ret;
354
}
355

356
error::Error UpdateModule::AsyncCallStateNoCapture(
591✔
357
        events::EventLoop &loop, State state, function<void(error::Error)> handler) {
358
        state_runner_.reset(new StateRunner(loop, state, GetModulePath(), GetModulesWorkPath()));
1,773✔
359

360
        return state_runner_->AsyncCallState(
591✔
361
                state,
362
                false,
363
                chrono::seconds(ctx_.GetConfig().module_timeout_seconds),
591✔
364
                [handler](expected::expected<optional<string>, error::Error> exp_output) {
10,621✔
365
                        if (!exp_output) {
590✔
366
                                handler(exp_output.error());
120✔
367
                        } else {
368
                                assert(!exp_output.value());
369
                                handler(error::NoError);
1,060✔
370
                        }
371
                });
1,772✔
372
}
373

374
error::Error UpdateModule::CallStateNoCapture(State state) {
180✔
375
        events::EventLoop loop;
376
        error::Error err;
180✔
377
        err = AsyncCallStateNoCapture(loop, state, [&err, &loop](error::Error inner_err) {
180✔
378
                err = inner_err;
179✔
379
                loop.Stop();
179✔
380
        });
180✔
381

382
        if (err != error::NoError) {
180✔
383
                return err;
384
        }
385

386
        loop.Run();
179✔
387

388
        state_runner_.reset();
389

390
        return err;
179✔
391
}
392

393
void UpdateModule::SetSystemRebootRunner(unique_ptr<SystemRebootRunner> &&system_reboot_runner) {
3✔
394
        system_reboot_ = std::move(system_reboot_runner);
395
}
3✔
396

397
} // namespace v3
398
} // namespace update_module
399
} // namespace update
400
} // 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