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

mendersoftware / mender / 968408678

pending completion
968408678

push

gitlab-ci

oleorhagen
feat(artifact/scripts): Add support for executing state scripts

Ticket: MEN-6636
Changelog: None

Signed-off-by: Ole Petter <ole.orhagen@northern.tech>

126 of 126 new or added lines in 4 files covered. (100.0%)

5405 of 6857 relevant lines covered (78.82%)

195.75 hits per line

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

91.72
/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) {
1,267✔
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)];
1,267✔
50
}
51

52
UpdateModule::UpdateModule(MenderContext &ctx, const string &payload_type) :
×
53
        ctx_ {ctx} {
×
54
        update_module_path_ =
55
                path::Join(ctx.GetConfig().default_config.GetDefaultModulesPath(), payload_type);
×
56
        update_module_workdir_ = path::Join(
×
57
                ctx.GetConfig().default_config.GetDefaultModulesWorkPath(),
×
58
                "modules",
59
                "v3",
60
                "payloads",
61
                "0000",
62
                "tree");
×
63
}
×
64

65
void UpdateModule::Cancel() {
×
66
        download_.reset();
×
67
        state_runner_.reset();
×
68
}
×
69

70
UpdateModule::DownloadData::DownloadData(
65✔
71
        events::EventLoop &event_loop, artifact::Payload &payload) :
65✔
72
        payload_ {payload},
73
        event_loop_ {event_loop} {
65✔
74
        buffer_.resize(MENDER_BUFSIZE);
65✔
75
}
65✔
76

77
error::Error UpdateModule::Download(artifact::Payload &payload) {
41✔
78
        events::EventLoop event_loop;
82✔
79
        error::Error err;
41✔
80
        AsyncDownload(event_loop, payload, [&event_loop, &err](error::Error inner_err) {
41✔
81
                err = inner_err;
41✔
82
                event_loop.Stop();
41✔
83
        });
82✔
84
        event_loop.Run();
41✔
85
        return err;
82✔
86
}
87

88
void UpdateModule::AsyncDownload(
65✔
89
        events::EventLoop &event_loop,
90
        artifact::Payload &payload,
91
        UpdateModule::StateFinishedHandler handler) {
92
        download_ = make_unique<DownloadData>(event_loop, payload);
65✔
93

94
        download_->download_finished_handler_ = [this, handler](error::Error err) {
65✔
95
                handler(err);
65✔
96
                download_.reset();
65✔
97
        };
195✔
98

99
        download_->event_loop_.Post([this]() { StartDownloadProcess(); });
130✔
100
}
65✔
101

102
error::Error UpdateModule::ArtifactInstall() {
29✔
103
        return CallStateNoCapture(State::ArtifactInstall);
29✔
104
}
105

106
error::Error UpdateModule::AsyncArtifactInstall(
21✔
107
        events::EventLoop &event_loop, StateFinishedHandler handler) {
108
        return AsyncCallStateNoCapture(event_loop, State::ArtifactInstall, handler);
21✔
109
}
110

111
static ExpectedRebootAction HandleNeedsRebootOutput(const expected::ExpectedString &exp_output) {
65✔
112
        if (!exp_output) {
65✔
113
                return expected::unexpected(error::Error(exp_output.error()));
6✔
114
        }
115
        auto &processStdOut = exp_output.value();
62✔
116
        if (processStdOut == "Yes") {
62✔
117
                return RebootAction::Yes;
31✔
118
        } else if (processStdOut == "No" || processStdOut == "") {
31✔
119
                return RebootAction::No;
27✔
120
        } else if (processStdOut == "Automatic") {
4✔
121
                return RebootAction::Automatic;
3✔
122
        }
123
        return expected::unexpected(error::Error(
1✔
124
                make_error_condition(errc::protocol_error),
1✔
125
                "Unexpected output from the process for NeedsReboot state"));
3✔
126
}
127

128
ExpectedRebootAction UpdateModule::NeedsReboot() {
29✔
129
        return HandleNeedsRebootOutput(CallStateCapture(State::NeedsReboot));
58✔
130
}
131

132
error::Error UpdateModule::AsyncNeedsReboot(
36✔
133
        events::EventLoop &event_loop, NeedsRebootFinishedHandler handler) {
134
        return AsyncCallStateCapture(
135
                event_loop, State::NeedsReboot, [handler](expected::ExpectedString exp_output) {
36✔
136
                        handler(HandleNeedsRebootOutput(exp_output));
36✔
137
                });
72✔
138
}
139

140
error::Error UpdateModule::ArtifactReboot() {
1✔
141
        return CallStateNoCapture(State::ArtifactReboot);
1✔
142
}
143

144
error::Error UpdateModule::AsyncArtifactReboot(
13✔
145
        events::EventLoop &event_loop, StateFinishedHandler handler) {
146
        return AsyncCallStateNoCapture(event_loop, State::ArtifactReboot, handler);
13✔
147
}
148

149
error::Error UpdateModule::ArtifactCommit() {
24✔
150
        return CallStateNoCapture(State::ArtifactCommit);
24✔
151
}
152

153
error::Error UpdateModule::AsyncArtifactCommit(
8✔
154
        events::EventLoop &event_loop, StateFinishedHandler handler) {
155
        return AsyncCallStateNoCapture(event_loop, State::ArtifactCommit, handler);
8✔
156
}
157

158
static expected::ExpectedBool HandleSupportsRollbackOutput(
59✔
159
        const expected::ExpectedString &exp_output) {
160
        if (!exp_output) {
59✔
161
                return expected::unexpected(error::Error(exp_output.error()));
4✔
162
        }
163
        auto &processStdOut = exp_output.value();
57✔
164
        if (processStdOut == "Yes") {
57✔
165
                return true;
35✔
166
        } else if (processStdOut == "No" || processStdOut == "") {
22✔
167
                return false;
21✔
168
        }
169
        return expected::unexpected(error::Error(
1✔
170
                make_error_condition(errc::protocol_error),
1✔
171
                "Unexpected output from the process for SupportsRollback state"));
3✔
172
}
173

174
expected::ExpectedBool UpdateModule::SupportsRollback() {
36✔
175
        return HandleSupportsRollbackOutput(CallStateCapture(State::SupportsRollback));
72✔
176
}
177

178
error::Error UpdateModule::AsyncSupportsRollback(
23✔
179
        events::EventLoop &event_loop, SupportsRollbackFinishedHandler handler) {
180
        return AsyncCallStateCapture(
181
                event_loop, State::SupportsRollback, [handler](expected::ExpectedString exp_output) {
23✔
182
                        handler(HandleSupportsRollbackOutput(exp_output));
23✔
183
                });
46✔
184
}
185

186
error::Error UpdateModule::ArtifactRollback() {
7✔
187
        return CallStateNoCapture(State::ArtifactRollback);
7✔
188
}
189

190
error::Error UpdateModule::AsyncArtifactRollback(
21✔
191
        events::EventLoop &event_loop, StateFinishedHandler handler) {
192
        return AsyncCallStateNoCapture(event_loop, State::ArtifactRollback, handler);
21✔
193
}
194

195
error::Error UpdateModule::ArtifactVerifyReboot() {
1✔
196
        return CallStateNoCapture(State::ArtifactVerifyReboot);
1✔
197
}
198

199
error::Error UpdateModule::AsyncArtifactVerifyReboot(
14✔
200
        events::EventLoop &event_loop, StateFinishedHandler handler) {
201
        return AsyncCallStateNoCapture(event_loop, State::ArtifactVerifyReboot, handler);
14✔
202
}
203

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

208
error::Error UpdateModule::AsyncArtifactRollbackReboot(
29✔
209
        events::EventLoop &event_loop, StateFinishedHandler handler) {
210
        return AsyncCallStateNoCapture(event_loop, State::ArtifactRollbackReboot, handler);
29✔
211
}
212

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

217
error::Error UpdateModule::AsyncArtifactVerifyRollbackReboot(
30✔
218
        events::EventLoop &event_loop, StateFinishedHandler handler) {
219
        return AsyncCallStateNoCapture(event_loop, State::ArtifactVerifyRollbackReboot, handler);
30✔
220
}
221

222
error::Error UpdateModule::ArtifactFailure() {
5✔
223
        return CallStateNoCapture(State::ArtifactFailure);
5✔
224
}
225

226
error::Error UpdateModule::AsyncArtifactFailure(
27✔
227
        events::EventLoop &event_loop, StateFinishedHandler handler) {
228
        return AsyncCallStateNoCapture(event_loop, State::ArtifactFailure, handler);
27✔
229
}
230

231
error::Error UpdateModule::Cleanup() {
28✔
232
        return CallStateNoCapture(State::Cleanup);
28✔
233
}
234

235
error::Error UpdateModule::AsyncCleanup(
38✔
236
        events::EventLoop &event_loop, StateFinishedHandler handler) {
237
        return AsyncCallStateNoCapture(event_loop, State::Cleanup, handler);
38✔
238
}
239

240
string UpdateModule::GetModulePath() const {
422✔
241
        return update_module_path_;
422✔
242
}
243

244
string UpdateModule::GetModulesWorkPath() const {
422✔
245
        return update_module_workdir_;
422✔
246
}
247

248
error::Error UpdateModule::GetProcessError(const error::Error &err) {
4✔
249
        if (err.code == make_error_condition(errc::no_such_file_or_directory)) {
4✔
250
                return context::MakeError(context::NoSuchUpdateModuleError, err.message);
×
251
        }
252
        return err;
4✔
253
}
254

255
error::Error UpdateModule::AsyncCallStateCapture(
124✔
256
        events::EventLoop &loop, State state, function<void(expected::ExpectedString)> handler) {
257
        state_runner_.reset(new StateRunner(loop, state, GetModulePath(), GetModulesWorkPath()));
124✔
258

259
        return state_runner_->AsyncCallState(
260
                state,
261
                true,
262
                chrono::seconds(ctx_.GetConfig().module_timeout_seconds),
124✔
263
                [handler](expected::expected<optional::optional<string>, error::Error> exp_output) {
124✔
264
                        if (!exp_output) {
124✔
265
                                handler(expected::unexpected(exp_output.error()));
5✔
266
                        } else {
267
                                assert(exp_output.value());
119✔
268
                                handler(exp_output.value().value());
119✔
269
                        }
270
                });
372✔
271
}
272

273
expected::ExpectedString UpdateModule::CallStateCapture(State state) {
65✔
274
        events::EventLoop loop;
130✔
275
        expected::ExpectedString ret;
130✔
276
        auto err = AsyncCallStateCapture(loop, state, [&ret, &loop](expected::ExpectedString str) {
130✔
277
                ret = str;
65✔
278
                loop.Stop();
65✔
279
        });
130✔
280

281
        if (err != error::NoError) {
65✔
282
                return expected::unexpected(err);
×
283
        }
284

285
        loop.Run();
65✔
286

287
        state_runner_.reset();
65✔
288

289
        return ret;
65✔
290
}
291

292
error::Error UpdateModule::AsyncCallStateNoCapture(
298✔
293
        events::EventLoop &loop, State state, function<void(error::Error)> handler) {
294
        state_runner_.reset(new StateRunner(loop, state, GetModulePath(), GetModulesWorkPath()));
298✔
295

296
        return state_runner_->AsyncCallState(
297
                state,
298
                false,
299
                chrono::seconds(ctx_.GetConfig().module_timeout_seconds),
298✔
300
                [handler](expected::expected<optional::optional<string>, error::Error> exp_output) {
297✔
301
                        if (!exp_output) {
297✔
302
                                handler(exp_output.error());
41✔
303
                        } else {
304
                                assert(!exp_output.value());
256✔
305
                                handler(error::NoError);
256✔
306
                        }
307
                });
893✔
308
}
309

310
error::Error UpdateModule::CallStateNoCapture(State state) {
97✔
311
        events::EventLoop loop;
194✔
312
        error::Error err;
97✔
313
        err = AsyncCallStateNoCapture(loop, state, [&err, &loop](error::Error inner_err) {
97✔
314
                err = inner_err;
96✔
315
                loop.Stop();
96✔
316
        });
194✔
317

318
        if (err != error::NoError) {
97✔
319
                return err;
1✔
320
        }
321

322
        loop.Run();
96✔
323

324
        state_runner_.reset();
96✔
325

326
        return err;
96✔
327
}
328

329
} // namespace v3
330
} // namespace update_module
331
} // namespace update
332
} // 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