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

mendersoftware / mender / 1499142629

16 Oct 2024 06:58PM UTC coverage: 76.305% (-0.06%) from 76.361%
1499142629

push

gitlab-ci

lluiscampos
fix: Invalidate cached inventory

Changelog: Invalidate cached inventory data on unauthentication event
to prevent an issue with which the client would not send inventory
data to the server after being unauthorized and authorized again.

Ticket: MEN-7617

Signed-off-by: Lluis Campos <lluis.campos@northern.tech>

0 of 4 new or added lines in 1 file covered. (0.0%)

268 existing lines in 7 files now uncovered.

7310 of 9580 relevant lines covered (76.3%)

11291.79 hits per line

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

84.21
/src/mender-update/standalone/standalone.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/standalone.hpp>
16

17
#include <common/common.hpp>
18
#include <common/events_io.hpp>
19
#include <common/http.hpp>
20
#include <common/log.hpp>
21
#include <common/path.hpp>
22

23
#include <artifact/v3/scripts/executor.hpp>
24

25
namespace mender {
26
namespace update {
27
namespace standalone {
28

29
using namespace std;
30

31
namespace common = mender::common;
32
namespace events = mender::common::events;
33
namespace executor = mender::artifact::scripts::executor;
34
namespace http = mender::common::http;
35
namespace io = mender::common::io;
36
namespace log = mender::common::log;
37
namespace path = mender::common::path;
38

39
ExpectedOptionalStateData LoadStateData(database::KeyValueDatabase &db) {
90✔
40
        StateDataKeys keys;
41
        StateData dst;
180✔
42

43
        auto exp_bytes = db.Read(context::MenderContext::standalone_state_key);
90✔
44
        if (!exp_bytes) {
90✔
45
                auto &err = exp_bytes.error();
46
                if (err.code == database::MakeError(database::KeyError, "").code) {
64✔
47
                        return optional<StateData>();
128✔
48
                } else {
49
                        return expected::unexpected(err);
×
50
                }
51
        }
52

53
        auto exp_json = json::Load(common::StringFromByteVector(exp_bytes.value()));
52✔
54
        if (!exp_json) {
26✔
55
                return expected::unexpected(exp_json.error());
×
56
        }
57
        auto &json = exp_json.value();
26✔
58

59
        auto exp_int = json::Get<int64_t>(json, keys.version, json::MissingOk::No);
26✔
60
        if (!exp_int) {
26✔
61
                return expected::unexpected(exp_int.error());
×
62
        }
63
        dst.version = exp_int.value();
26✔
64

65
        if (dst.version != 1 && dst.version != context::MenderContext::standalone_data_version) {
26✔
66
                return expected::unexpected(error::Error(
×
67
                        make_error_condition(errc::not_supported),
×
68
                        "State data has a version which is not supported by this client"));
×
69
        }
70

71
        auto exp_string = json::Get<string>(json, keys.artifact_name, json::MissingOk::No);
26✔
72
        if (!exp_string) {
26✔
73
                return expected::unexpected(exp_string.error());
×
74
        }
75
        dst.artifact_name = exp_string.value();
26✔
76

77
        exp_string = json::Get<string>(json, keys.artifact_group, json::MissingOk::Yes);
52✔
78
        if (!exp_string) {
26✔
79
                return expected::unexpected(exp_string.error());
×
80
        }
81
        dst.artifact_group = exp_string.value();
26✔
82

83
        auto exp_map = json::Get<json::KeyValueMap>(json, keys.artifact_provides, json::MissingOk::No);
26✔
84
        if (exp_map) {
26✔
85
                dst.artifact_provides = exp_map.value();
25✔
86
        } else {
87
                dst.artifact_provides.reset();
88
        }
89

90
        auto exp_array =
91
                json::Get<vector<string>>(json, keys.artifact_clears_provides, json::MissingOk::No);
26✔
92
        if (exp_array) {
26✔
93
                dst.artifact_clears_provides = exp_array.value();
25✔
94
        } else {
95
                dst.artifact_clears_provides.reset();
96
        }
97

98
        exp_array = json::Get<vector<string>>(json, keys.payload_types, json::MissingOk::No);
52✔
99
        if (!exp_array) {
26✔
100
                return expected::unexpected(exp_array.error());
×
101
        }
102
        dst.payload_types = exp_array.value();
26✔
103

104
        if (dst.version == 1) {
26✔
105
                // In version 1, if there is any data at all, it is equivalent to this:
106
                dst.in_state = StateData::kInStateArtifactCommit_Enter;
107
                dst.failed = false;
×
108
                dst.rolled_back = false;
×
109

110
                // Additionally, there is never any situation where we want to save version 1 data,
111
                // because it only has one state: The one we just loaded in the previous
112
                // statement. In a rollback situation, all states are always carried out and the
113
                // data is removed instead. Therefore, always set it to version 2, so we can't even
114
                // theoretically save it wrongly (and we don't need to handle it in the saving
115
                // code).
116
                dst.version = context::MenderContext::standalone_data_version;
×
117
        } else {
118
                exp_string = json::Get<string>(json, keys.in_state, json::MissingOk::No);
52✔
119
                if (!exp_string) {
26✔
120
                        return expected::unexpected(exp_string.error());
×
121
                }
122
                dst.in_state = exp_string.value();
26✔
123

124
                auto exp_bool = json::Get<bool>(json, keys.failed, json::MissingOk::No);
26✔
125
                if (!exp_bool) {
26✔
126
                        return expected::unexpected(exp_bool.error());
×
127
                }
128
                dst.failed = exp_bool.value();
26✔
129

130
                exp_bool = json::Get<bool>(json, keys.rolled_back, json::MissingOk::No);
52✔
131
                if (!exp_bool) {
26✔
132
                        return expected::unexpected(exp_bool.error());
×
133
                }
134
                dst.rolled_back = exp_bool.value();
26✔
135
        }
136

137
        if (dst.artifact_name == "") {
26✔
138
                return expected::unexpected(context::MakeError(
×
139
                        context::DatabaseValueError, "`" + keys.artifact_name + "` is empty"));
×
140
        }
141

142
        if (dst.payload_types.size() == 0) {
26✔
143
                return expected::unexpected(context::MakeError(
×
144
                        context::DatabaseValueError, "`" + keys.payload_types + "` is empty"));
×
145
        }
146
        if (dst.payload_types.size() >= 2) {
26✔
147
                return expected::unexpected(error::Error(
×
148
                        make_error_condition(errc::not_supported),
×
149
                        "`" + keys.payload_types + "` contains multiple payloads"));
×
150
        }
151

152
        return dst;
26✔
153
}
154

155
error::Error SaveStateData(database::KeyValueDatabase &db, const StateData &data) {
386✔
156
        return db.WriteTransaction(
157
                [&data](database::Transaction &txn) { return SaveStateData(txn, data); });
1,158✔
158
}
159

160
error::Error SaveStateData(database::Transaction &txn, const StateData &data) {
387✔
161
        StateDataKeys keys;
162
        stringstream ss;
774✔
163
        ss << "{";
387✔
164
        ss << "\"" << keys.version << "\":" << data.version;
387✔
165

166
        ss << ",";
387✔
167
        ss << "\"" << keys.artifact_name << "\":\"" << data.artifact_name << "\"";
387✔
168

169
        ss << ",";
387✔
170
        ss << "\"" << keys.artifact_group << "\":\"" << data.artifact_group << "\"";
387✔
171

172
        ss << ",";
387✔
173
        ss << "\"" << keys.payload_types << "\": [";
387✔
174
        bool first = true;
175
        for (auto elem : data.payload_types) {
774✔
176
                if (!first) {
387✔
177
                        ss << ",";
×
178
                }
179
                ss << "\"" << elem << "\"";
387✔
180
                first = false;
181
        }
182
        ss << "]";
387✔
183

184
        if (data.artifact_provides) {
387✔
185
                ss << ",";
379✔
186
                ss << "\"" << keys.artifact_provides << "\": {";
379✔
187
                bool first = true;
188
                for (auto elem : data.artifact_provides.value()) {
2,274✔
189
                        if (!first) {
758✔
190
                                ss << ",";
379✔
191
                        }
192
                        ss << "\"" << elem.first << "\":\"" << elem.second << "\"";
758✔
193
                        first = false;
194
                }
195
                ss << "}";
379✔
196
        }
197

198
        if (data.artifact_clears_provides) {
387✔
199
                ss << ",";
379✔
200
                ss << "\"" << keys.artifact_clears_provides << "\": [";
379✔
201
                bool first = true;
202
                for (auto elem : data.artifact_clears_provides.value()) {
1,895✔
203
                        if (!first) {
1,137✔
204
                                ss << ",";
758✔
205
                        }
206
                        ss << "\"" << elem << "\"";
1,137✔
207
                        first = false;
208
                }
209
                ss << "]";
379✔
210
        }
211

212
        ss << R"(,")" << keys.in_state << R"(":")" << data.in_state << R"(")";
387✔
213

214
        ss << R"(,")" << keys.failed << R"(":)" << (data.failed ? "true" : "false");
739✔
215

216
        ss << R"(,")" << keys.rolled_back << R"(":)" << (data.rolled_back ? "true" : "false");
752✔
217

218
        ss << "}";
387✔
219

220
        string strdata = ss.str();
221
        vector<uint8_t> bytedata(common::ByteVectorFromString(strdata));
387✔
222

223
        return txn.Write(context::MenderContext::standalone_state_key, bytedata);
774✔
224
}
225

226
StateMachine::StateMachine(Context &ctx) :
87✔
227
        context_ {ctx},
228
        download_enter_state_ {
229
                executor::State::Download,
230
                executor::Action::Enter,
231
                executor::OnError::Fail,
232
                Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary},
233
        download_leave_state_ {
234
                executor::State::Download,
235
                executor::Action::Leave,
236
                executor::OnError::Fail,
237
                Result::DownloadFailed | Result::Failed | Result::NoRollbackNecessary},
238
        download_error_state_ {
239
                executor::State::Download,
240
                executor::Action::Error,
241
                executor::OnError::Ignore,
242
                Result::NoResult},
243
        save_before_artifact_install_state_ {StateData::kBeforeStateArtifactInstall_Enter},
244
        save_artifact_install_state_ {StateData::kInStateArtifactInstall_Enter},
245
        artifact_install_enter_state_ {
246
                executor::State::ArtifactInstall,
247
                executor::Action::Enter,
248
                executor::OnError::Fail,
249
                Result::InstallFailed | Result::Failed},
250
        artifact_install_leave_state_ {
251
                executor::State::ArtifactInstall,
252
                executor::Action::Leave,
253
                executor::OnError::Fail,
254
                Result::InstallFailed | Result::Failed},
255
        artifact_install_error_state_ {
256
                executor::State::ArtifactInstall,
257
                executor::Action::Error,
258
                executor::OnError::Ignore,
259
                Result::NoResult},
260
        save_before_artifact_commit_state_ {StateData::kBeforeStateArtifactCommit_Enter},
261
        save_artifact_commit_state_ {StateData::kInStateArtifactCommit_Enter},
262
        artifact_commit_enter_state_ {
263
                executor::State::ArtifactCommit,
264
                executor::Action::Enter,
265
                executor::OnError::Fail,
266
                Result::CommitFailed | Result::Failed},
267
        save_before_artifact_commit_leave_state_ {StateData::kBeforeStateArtifactCommit_Leave},
268
        save_artifact_commit_leave_state_ {StateData::kInStateArtifactCommit_Leave},
269
        artifact_commit_leave_state_ {
270
                executor::State::ArtifactCommit,
271
                executor::Action::Leave,
272
                executor::OnError::Ignore,
273
                Result::CommitFailed | Result::Failed | Result::FailedInPostCommit},
274
        artifact_commit_error_state_ {
275
                executor::State::ArtifactCommit,
276
                executor::Action::Error,
277
                executor::OnError::Ignore,
278
                Result::NoResult},
279
        save_artifact_rollback_state_ {StateData::kInStateArtifactRollback_Enter},
280
        artifact_rollback_enter_state_ {
281
                executor::State::ArtifactRollback,
282
                executor::Action::Enter,
283
                executor::OnError::Ignore,
284
                Result::Failed | Result::RollbackFailed},
285
        artifact_rollback_leave_state_ {
286
                executor::State::ArtifactRollback,
287
                executor::Action::Leave,
288
                executor::OnError::Ignore,
289
                Result::NoResult},
290
        save_artifact_failure_state_ {StateData::kInStateArtifactFailure_Enter},
291
        artifact_failure_enter_state_ {
292
                executor::State::ArtifactFailure,
293
                executor::Action::Enter,
294
                executor::OnError::Ignore,
295
                Result::Failed | Result::RollbackFailed},
296
        artifact_failure_leave_state_ {
297
                executor::State::ArtifactFailure,
298
                executor::Action::Leave,
299
                executor::OnError::Ignore,
300
                Result::NoResult},
301
        save_cleanup_state_ {StateData::kInStateCleanup},
302
        exit_state_ {loop_},
87✔
303
        start_state_ {&prepare_download_state_},
304
        state_machine_ {*start_state_} {
87✔
305
        using tf = common::state_machine::TransitionFlag;
306
        using se = StateEvent;
307
        auto &s = state_machine_;
308

309
        // clang-format off
310
        s.AddTransition(prepare_download_state_,                  se::Success,              download_enter_state_,                    tf::Immediate);
87✔
311
        s.AddTransition(prepare_download_state_,                  se::Failure,              exit_state_,                              tf::Immediate);
87✔
312
        s.AddTransition(prepare_download_state_,                  se::EmptyPayloadArtifact, exit_state_,                              tf::Immediate);
87✔
313

314
        s.AddTransition(download_enter_state_,                    se::Success,              download_state_,                          tf::Immediate);
87✔
315
        s.AddTransition(download_enter_state_,                    se::Failure,              download_error_state_,                    tf::Immediate);
87✔
316

317
        s.AddTransition(download_state_,                          se::Success,              download_leave_state_,                    tf::Immediate);
87✔
318
        s.AddTransition(download_state_,                          se::Failure,              download_error_state_,                    tf::Immediate);
87✔
319

320
        s.AddTransition(download_leave_state_,                    se::Success,              save_before_artifact_install_state_,      tf::Immediate);
87✔
321
        s.AddTransition(download_leave_state_,                    se::Failure,              download_error_state_,                    tf::Immediate);
87✔
322

323
        s.AddTransition(download_error_state_,                    se::Success,              save_cleanup_state_,                      tf::Immediate);
87✔
324
        s.AddTransition(download_error_state_,                    se::Failure,              save_cleanup_state_,                      tf::Immediate);
87✔
325

326
        // The reason we have a "save_before" state followed by a "save" state is the
327
        // `--stop-before` argument. We want to make sure that:
328

329
        // 1. If you specify the flag twice in a row, the second run is a noop (just stops at the
330
        //    same point). This is accomplished using the "save_before" state, which we return to
331
        //    during a DB recovery.
332

333
        // 2. If we have started executing the following states, it should no longer we possible to
334
        //    use the `--stop-before` flag for that state, since the state execution has started
335
        //    already. This is done by saving a different value in the "save" state, and is thus
336
        //    preserved even after a spontaneous reboot. Once we have gone there, there is no going
337
        //    back.
338
        s.AddTransition(save_before_artifact_install_state_,      se::Success,              save_artifact_install_state_,             tf::Immediate);
87✔
339
        s.AddTransition(save_before_artifact_install_state_,      se::Failure,              save_cleanup_state_,                      tf::Immediate);
87✔
340

341
        s.AddTransition(save_artifact_install_state_,             se::Success,              artifact_install_enter_state_,            tf::Immediate);
87✔
342
        s.AddTransition(save_artifact_install_state_,             se::Failure,              save_cleanup_state_,                      tf::Immediate);
87✔
343

344
        s.AddTransition(artifact_install_enter_state_,            se::Success,              artifact_install_state_,                  tf::Immediate);
87✔
345
        s.AddTransition(artifact_install_enter_state_,            se::Failure,              artifact_install_error_state_,            tf::Immediate);
87✔
346

347
        s.AddTransition(artifact_install_state_,                  se::Success,              artifact_install_leave_state_,            tf::Immediate);
87✔
348
        s.AddTransition(artifact_install_state_,                  se::Failure,              artifact_install_error_state_,            tf::Immediate);
87✔
349

350
        s.AddTransition(artifact_install_leave_state_,            se::Success,              reboot_and_rollback_query_state_,         tf::Immediate);
87✔
351
        s.AddTransition(artifact_install_leave_state_,            se::Failure,              artifact_install_error_state_,            tf::Immediate);
87✔
352

353
        s.AddTransition(artifact_install_error_state_,            se::Success,              rollback_query_state_,                    tf::Immediate);
87✔
354
        s.AddTransition(artifact_install_error_state_,            se::Failure,              rollback_query_state_,                    tf::Immediate);
87✔
355

356
        s.AddTransition(reboot_and_rollback_query_state_,         se::Success,              save_before_artifact_commit_state_,       tf::Immediate);
87✔
357
        s.AddTransition(reboot_and_rollback_query_state_,         se::Failure,              rollback_query_state_,                    tf::Immediate);
87✔
358
        s.AddTransition(reboot_and_rollback_query_state_,         se::NeedsInteraction,     exit_state_,                              tf::Immediate);
87✔
359

360
        // See `save_before_artifact_install_state_` for an explanation of the following two states.
361
        s.AddTransition(save_before_artifact_commit_state_,       se::Success,              save_artifact_commit_state_,              tf::Immediate);
87✔
362
        s.AddTransition(save_before_artifact_commit_state_,       se::Failure,              rollback_query_state_,                    tf::Immediate);
87✔
363

364
        s.AddTransition(save_artifact_commit_state_,              se::Success,              artifact_commit_enter_state_,             tf::Immediate);
87✔
365
        s.AddTransition(save_artifact_commit_state_,              se::Failure,              rollback_query_state_,                    tf::Immediate);
87✔
366

367
        s.AddTransition(artifact_commit_enter_state_,             se::Success,              artifact_commit_state_,                   tf::Immediate);
87✔
368
        s.AddTransition(artifact_commit_enter_state_,             se::Failure,              artifact_commit_error_state_,             tf::Immediate);
87✔
369

370
        s.AddTransition(artifact_commit_state_,                   se::Success,              save_before_artifact_commit_leave_state_, tf::Immediate);
87✔
371
        s.AddTransition(artifact_commit_state_,                   se::Failure,              artifact_commit_error_state_,             tf::Immediate);
87✔
372

373
        // See `save_before_artifact_install_state_` for an explanation of the following two states.
374
        s.AddTransition(save_before_artifact_commit_leave_state_, se::Success,              save_artifact_commit_leave_state_,        tf::Immediate);
87✔
375
        s.AddTransition(save_before_artifact_commit_leave_state_, se::Failure,              artifact_commit_error_state_,             tf::Immediate);
87✔
376

377
        s.AddTransition(save_artifact_commit_leave_state_,        se::Success,              artifact_commit_leave_state_,             tf::Immediate);
87✔
378
        s.AddTransition(save_artifact_commit_leave_state_,        se::Failure,              artifact_commit_error_state_,             tf::Immediate);
87✔
379

380
        s.AddTransition(artifact_commit_leave_state_,             se::Success,              save_cleanup_state_,                      tf::Immediate);
87✔
381
        s.AddTransition(artifact_commit_leave_state_,             se::Failure,              save_cleanup_state_,                      tf::Immediate);
87✔
382

383
        s.AddTransition(rollback_query_state_,                    se::Success,              save_artifact_rollback_state_,            tf::Immediate);
87✔
384
        s.AddTransition(rollback_query_state_,                    se::NothingToDo,          save_artifact_failure_state_,             tf::Immediate);
87✔
385
        s.AddTransition(rollback_query_state_,                    se::Failure,              save_artifact_failure_state_,             tf::Immediate);
87✔
386
        s.AddTransition(rollback_query_state_,                    se::NeedsInteraction,     exit_state_,                              tf::Immediate);
87✔
387

388
        s.AddTransition(artifact_commit_error_state_,             se::Success,              rollback_query_state_,                    tf::Immediate);
87✔
389
        s.AddTransition(artifact_commit_error_state_,             se::Failure,              rollback_query_state_,                    tf::Immediate);
87✔
390

391
        // Note: States on the error path are supposed to be idempotent, and may execute several
392
        // times if interrupted by a powerloss. Hence they don't need `save_before` states. See
393
        // `save_before_artifact_install_state_` for more context.
394
        s.AddTransition(save_artifact_rollback_state_,            se::Success,              artifact_rollback_enter_state_,           tf::Immediate);
87✔
395
        s.AddTransition(save_artifact_rollback_state_,            se::Failure,              artifact_rollback_enter_state_,           tf::Immediate);
87✔
396

397
        s.AddTransition(artifact_rollback_enter_state_,           se::Success,              artifact_rollback_state_,                 tf::Immediate);
87✔
398
        s.AddTransition(artifact_rollback_enter_state_,           se::Failure,              artifact_rollback_state_,                 tf::Immediate);
87✔
399

400
        s.AddTransition(artifact_rollback_state_,                 se::Success,              artifact_rollback_leave_state_,           tf::Immediate);
87✔
401
        s.AddTransition(artifact_rollback_state_,                 se::Failure,              artifact_rollback_leave_state_,           tf::Immediate);
87✔
402

403
        s.AddTransition(artifact_rollback_leave_state_,           se::Success,              save_artifact_failure_state_,             tf::Immediate);
87✔
404
        s.AddTransition(artifact_rollback_leave_state_,           se::Failure,              save_artifact_failure_state_,             tf::Immediate);
87✔
405

406
        // See comment for `save_artifact_rollback_state_`.
407
        s.AddTransition(save_artifact_failure_state_,             se::Success,              artifact_failure_enter_state_,            tf::Immediate);
87✔
408
        s.AddTransition(save_artifact_failure_state_,             se::Failure,              artifact_failure_enter_state_,            tf::Immediate);
87✔
409

410
        s.AddTransition(artifact_failure_enter_state_,            se::Success,              artifact_failure_state_,                  tf::Immediate);
87✔
411
        s.AddTransition(artifact_failure_enter_state_,            se::Failure,              artifact_failure_state_,                  tf::Immediate);
87✔
412

413
        s.AddTransition(artifact_failure_state_,                  se::Success,              artifact_failure_leave_state_,            tf::Immediate);
87✔
414
        s.AddTransition(artifact_failure_state_,                  se::Failure,              artifact_failure_leave_state_,            tf::Immediate);
87✔
415

416
        s.AddTransition(artifact_failure_leave_state_,            se::Success,              save_cleanup_state_,                      tf::Immediate);
87✔
417
        s.AddTransition(artifact_failure_leave_state_,            se::Failure,              save_cleanup_state_,                      tf::Immediate);
87✔
418

419
        // See comment for `save_artifact_rollback_state_`. While cleanup is not strictly an error
420
        // state, it is idempotent.
421
        s.AddTransition(save_cleanup_state_,                      se::Success,              cleanup_state_,                           tf::Immediate);
87✔
422
        s.AddTransition(save_cleanup_state_,                      se::Failure,              cleanup_state_,                           tf::Immediate);
87✔
423

424
        s.AddTransition(cleanup_state_,                           se::Success,              exit_state_,                              tf::Immediate);
87✔
425
        s.AddTransition(cleanup_state_,                           se::Failure,              exit_state_,                              tf::Immediate);
87✔
426
        // clang-format on
427
}
87✔
428

429
void StateMachine::Run() {
87✔
430
        common::state_machine::StateMachineRunner<Context, StateEvent> runner {context_};
174✔
431
        runner.AddStateMachine(state_machine_);
87✔
432
        runner.AttachToEventLoop(loop_);
87✔
433

434
        state_machine_.SetState(*start_state_);
87✔
435

436
        loop_.Run();
87✔
437
}
87✔
438

439
error::Error StateMachine::SetStartStateFromStateData(const string &in_state) {
18✔
440
        if (in_state == StateData::kBeforeStateArtifactInstall_Enter) {
18✔
441
                start_state_ = &save_before_artifact_install_state_;
2✔
442
        } else if (in_state == StateData::kInStateArtifactInstall_Enter) {
16✔
UNCOV
443
                start_state_ = &save_artifact_rollback_state_;
×
444
        } else if (in_state == StateData::kBeforeStateArtifactCommit_Enter) {
16✔
445
                start_state_ = &save_before_artifact_commit_state_;
8✔
446
        } else if (in_state == StateData::kInStateArtifactCommit_Enter) {
8✔
UNCOV
447
                start_state_ = &save_artifact_rollback_state_;
×
448
        } else if (in_state == StateData::kBeforeStateArtifactCommit_Leave) {
8✔
449
                start_state_ = &save_before_artifact_commit_leave_state_;
3✔
450
        } else if (in_state == StateData::kInStateArtifactCommit_Leave) {
5✔
UNCOV
451
                start_state_ = &artifact_commit_leave_state_;
×
452
        } else if (in_state == StateData::kInStateCleanup) {
5✔
453
                start_state_ = &save_cleanup_state_;
2✔
454
        } else if (in_state == StateData::kInStateArtifactRollback_Enter) {
3✔
455
                start_state_ = &save_artifact_rollback_state_;
1✔
456
        } else if (in_state == StateData::kInStateArtifactFailure_Enter) {
2✔
457
                start_state_ = &save_artifact_failure_state_;
2✔
458
        } else {
459
                return context::MakeError(
UNCOV
460
                        context::DatabaseValueError, "Invalid InState in database " + in_state);
×
461
        }
462

463
        return error::NoError;
18✔
464
}
465

466
error::Error StateMachine::AddStopBeforeState(const string &state) {
16✔
467
        using tf = common::state_machine::TransitionFlag;
468
        using se = StateEvent;
469
        auto &s = state_machine_;
16✔
470

471
        // Replace transition in state machine in order to exit at given point.
472
        if (state == "ArtifactInstall_Enter") {
16✔
473
                s.AddTransition(
2✔
474
                        save_before_artifact_install_state_, se::Success, exit_state_, tf::Immediate);
475

476
        } else if (state == "ArtifactCommit_Enter") {
14✔
477
                s.AddTransition(
4✔
478
                        save_before_artifact_commit_state_, se::Success, exit_state_, tf::Immediate);
479

480
        } else if (state == "ArtifactCommit_Leave") {
10✔
481
                s.AddTransition(
4✔
482
                        save_before_artifact_commit_leave_state_, se::Success, exit_state_, tf::Immediate);
483

484
        } else if (state == "ArtifactRollback_Enter") {
6✔
485
                s.AddTransition(save_artifact_rollback_state_, se::Success, exit_state_, tf::Immediate);
2✔
486
                s.AddTransition(save_artifact_rollback_state_, se::Failure, exit_state_, tf::Immediate);
2✔
487

488
        } else if (state == "ArtifactFailure_Enter") {
4✔
489
                s.AddTransition(save_artifact_failure_state_, se::Success, exit_state_, tf::Immediate);
2✔
490
                s.AddTransition(save_artifact_failure_state_, se::Failure, exit_state_, tf::Immediate);
2✔
491

492
        } else if (state == "Cleanup") {
2✔
493
                s.AddTransition(save_cleanup_state_, se::Success, exit_state_, tf::Immediate);
2✔
494
                s.AddTransition(save_cleanup_state_, se::Failure, exit_state_, tf::Immediate);
2✔
495

UNCOV
496
        } else if (state != "") {
×
497
                return context::MakeError(
UNCOV
498
                        context::ValueError, "Cannot stop before unsupported state " + state);
×
499
        }
500

501
        return error::NoError;
16✔
502
}
503

UNCOV
504
void StateMachine::StartOnRollback() {
×
505
        start_state_ = &rollback_query_state_;
7✔
506
}
×
507

508
error::Error PrepareContext(Context &ctx) {
87✔
509
        const auto &conf = ctx.main_context.GetConfig();
87✔
510
        const auto &paths {conf.paths};
511
        ctx.script_runner.reset(new executor::ScriptRunner(
512
                ctx.loop,
513
                chrono::seconds {conf.state_script_timeout_seconds},
514
                chrono::seconds {conf.state_script_retry_interval_seconds},
515
                chrono::seconds {conf.state_script_retry_timeout_seconds},
516
                paths.GetArtScriptsPath(),
87✔
517
                paths.GetRootfsScriptsPath()));
174✔
518

519
        return error::NoError;
87✔
520
}
521

522
error::Error PrepareContextFromStateData(Context &ctx, const StateData &data) {
25✔
523
        ctx.update_module.reset(
524
                new update_module::UpdateModule(ctx.main_context, data.payload_types[0]));
25✔
525

526
        if (data.payload_types[0] == "rootfs-image") {
25✔
527
                // Special case for rootfs-image upgrades. See comments inside the function.
528
                auto err = ctx.update_module->EnsureRootfsImageFileTree(
529
                        ctx.update_module->GetUpdateModuleWorkDir());
25✔
530
                if (err != error::NoError) {
25✔
UNCOV
531
                        return err;
×
532
                }
533
        }
534

535
        if (data.failed) {
25✔
UNCOV
536
                ctx.result_and_error.result = ctx.result_and_error.result | Result::Failed;
×
537
        }
538

539
        if (data.rolled_back) {
25✔
540
                ctx.result_and_error.result = ctx.result_and_error.result | Result::RolledBack;
4✔
541
        }
542

543
        return error::NoError;
25✔
544
}
545

546
ResultAndError Install(
63✔
547
        Context &ctx,
548
        const string &src,
549
        const artifact::config::Signature verify_signature,
550
        InstallOptions options) {
551
        auto exp_in_progress = LoadStateData(ctx.main_context.GetMenderStoreDB());
63✔
552
        if (!exp_in_progress) {
63✔
UNCOV
553
                return {Result::Failed, exp_in_progress.error()};
×
554
        }
555
        auto &in_progress = exp_in_progress.value();
63✔
556

557
        if (in_progress) {
63✔
558
                return {
559
                        Result::Failed | Result::NoRollbackNecessary,
560
                        error::Error(
561
                                make_error_condition(errc::operation_in_progress),
2✔
562
                                "Update already in progress. Please commit or roll back first")};
2✔
563
        }
564

565
        auto err = PrepareContext(ctx);
62✔
566
        if (err != error::NoError) {
62✔
UNCOV
567
                return {Result::Failed, err};
×
568
        }
569

570
        ctx.artifact_src = src;
62✔
571
        ctx.verify_signature = verify_signature;
62✔
572
        ctx.options = options;
62✔
573

574
        StateMachine state_machine {ctx};
124✔
575

576
        for (const auto &state : ctx.stop_before) {
67✔
577
                err = state_machine.AddStopBeforeState(state);
5✔
578
                if (err != error::NoError) {
5✔
579
                        return {Result::Failed, err};
×
580
                }
581
        }
582

583
        state_machine.Run();
62✔
584

585
        return ctx.result_and_error;
62✔
586
}
587

588
ResultAndError Resume(Context &ctx) {
13✔
589
        auto exp_in_progress = LoadStateData(ctx.main_context.GetMenderStoreDB());
13✔
590
        if (!exp_in_progress) {
13✔
UNCOV
591
                return {Result::Failed, exp_in_progress.error()};
×
592
        }
593
        auto &in_progress = exp_in_progress.value();
13✔
594

595
        if (!in_progress) {
13✔
596
                return {
597
                        Result::NoUpdateInProgress,
UNCOV
598
                        context::MakeError(context::NoUpdateInProgressError, "Cannot resume")};
×
599
        }
600
        ctx.state_data = in_progress.value();
13✔
601

602
        auto err = PrepareContext(ctx);
13✔
603
        if (err != error::NoError) {
13✔
604
                return {Result::Failed, err};
×
605
        }
606

607
        err = PrepareContextFromStateData(ctx, ctx.state_data);
13✔
608
        if (err != error::NoError) {
13✔
UNCOV
609
                return {Result::Failed, err};
×
610
        }
611

612
        StateMachine state_machine {ctx};
26✔
613

614
        err = state_machine.SetStartStateFromStateData(ctx.state_data.in_state);
13✔
615
        if (err != error::NoError) {
13✔
UNCOV
616
                return {Result::Failed, err};
×
617
        }
618

619
        for (const auto &state : ctx.stop_before) {
21✔
620
                err = state_machine.AddStopBeforeState(state);
8✔
621
                if (err != error::NoError) {
8✔
UNCOV
622
                        return {Result::Failed, err};
×
623
                }
624
        }
625

626
        state_machine.Run();
13✔
627

628
        return ctx.result_and_error;
13✔
629
}
630

631
ResultAndError Commit(Context &ctx) {
6✔
632
        auto exp_in_progress = LoadStateData(ctx.main_context.GetMenderStoreDB());
6✔
633
        if (!exp_in_progress) {
6✔
UNCOV
634
                return {Result::Failed, exp_in_progress.error()};
×
635
        }
636
        auto &in_progress = exp_in_progress.value();
6✔
637

638
        if (!in_progress) {
6✔
639
                return {
640
                        Result::NoUpdateInProgress,
641
                        context::MakeError(context::NoUpdateInProgressError, "Cannot commit")};
2✔
642
        }
643
        ctx.state_data = in_progress.value();
5✔
644

645
        if (ctx.state_data.in_state != StateData::kBeforeStateArtifactCommit_Enter
5✔
646
                and ctx.state_data.in_state != StateData::kInStateArtifactCommit_Enter) {
5✔
647
                return {
648
                        Result::Failed,
649
                        context::MakeError(
650
                                context::WrongOperationError,
651
                                "Cannot commit from this state. "
UNCOV
652
                                "Make sure that the `install` command has run successfully and the device is expecting a commit.")};
×
653
        }
654

655
        auto err = PrepareContext(ctx);
5✔
656
        if (err != error::NoError) {
5✔
UNCOV
657
                return {Result::Failed, err};
×
658
        }
659

660
        err = PrepareContextFromStateData(ctx, ctx.state_data);
5✔
661
        if (err != error::NoError) {
5✔
UNCOV
662
                return {Result::Failed, err};
×
663
        }
664

665
        StateMachine state_machine {ctx};
10✔
666

667
        err = state_machine.SetStartStateFromStateData(ctx.state_data.in_state);
5✔
668
        if (err != error::NoError) {
5✔
UNCOV
669
                return {Result::Failed, err};
×
670
        }
671

672
        for (const auto &state : ctx.stop_before) {
7✔
673
                err = state_machine.AddStopBeforeState(state);
2✔
674
                if (err != error::NoError) {
2✔
UNCOV
675
                        return {Result::Failed, err};
×
676
                }
677
        }
678

679
        state_machine.Run();
5✔
680

681
        return ctx.result_and_error;
5✔
682
}
683

684
ResultAndError Rollback(Context &ctx) {
8✔
685
        auto exp_in_progress = LoadStateData(ctx.main_context.GetMenderStoreDB());
8✔
686
        if (!exp_in_progress) {
8✔
UNCOV
687
                return {Result::Failed, exp_in_progress.error()};
×
688
        }
689
        auto &in_progress = exp_in_progress.value();
8✔
690

691
        if (!in_progress) {
8✔
692
                return {
693
                        Result::NoUpdateInProgress,
694
                        context::MakeError(context::NoUpdateInProgressError, "Cannot roll back")};
2✔
695
        }
696
        ctx.state_data = in_progress.value();
7✔
697

698
        if (ctx.state_data.in_state != StateData::kBeforeStateArtifactInstall_Enter
7✔
699
                and ctx.state_data.in_state != StateData::kInStateArtifactInstall_Enter
7✔
700
                and ctx.state_data.in_state != StateData::kBeforeStateArtifactCommit_Enter
7✔
701
                and ctx.state_data.in_state != StateData::kInStateArtifactCommit_Enter
2✔
702
                and ctx.state_data.in_state != StateData::kBeforeStateArtifactCommit_Leave
2✔
703
                and ctx.state_data.in_state != StateData::kInStateArtifactRollback_Enter) {
8✔
704
                return {
705
                        Result::Failed,
706
                        context::MakeError(
707
                                context::WrongOperationError,
708
                                "Cannot roll back from this state. "
UNCOV
709
                                "Use `resume` to complete the current install in order to start a new one.")};
×
710
        }
711

712
        auto err = PrepareContext(ctx);
7✔
713
        if (err != error::NoError) {
7✔
UNCOV
714
                return {Result::Failed, err};
×
715
        }
716

717
        err = PrepareContextFromStateData(ctx, ctx.state_data);
7✔
718
        if (err != error::NoError) {
7✔
UNCOV
719
                return {Result::Failed, err};
×
720
        }
721

722
        StateMachine state_machine {ctx};
14✔
723
        state_machine.StartOnRollback();
724
        if (err != error::NoError) {
7✔
UNCOV
725
                return {Result::Failed, err};
×
726
        }
727

728
        for (const auto &state : ctx.stop_before) {
8✔
729
                err = state_machine.AddStopBeforeState(state);
1✔
730
                if (err != error::NoError) {
1✔
UNCOV
731
                        return {Result::Failed, err};
×
732
                }
733
        }
734

735
        state_machine.Run();
7✔
736

737
        return ctx.result_and_error;
7✔
738
}
739

740
} // namespace standalone
741
} // namespace update
742
} // 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