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

mendersoftware / mender / 974575668

21 Aug 2023 12:04PM UTC coverage: 78.829% (-0.05%) from 78.877%
974575668

push

gitlab-ci

kacf
chore: Implement pushing of logs to the server.

Ticket: MEN-6581

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

18 of 18 new or added lines in 2 files covered. (100.0%)

5492 of 6967 relevant lines covered (78.83%)

238.75 hits per line

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

95.86
/mender-update/daemon/state_machine/state_machine.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 <common/conf.hpp>
16
#include <common/log.hpp>
17

18
#include <mender-update/daemon/states.hpp>
19
#include <mender-update/daemon/state_machine.hpp>
20

21
namespace mender {
22
namespace update {
23
namespace daemon {
24

25
namespace conf = mender::common::conf;
26
namespace log = mender::common::log;
27

28
StateMachine::StateMachine(Context &ctx, events::EventLoop &event_loop) :
47✔
29
        ctx_(ctx),
30
        event_loop_(event_loop),
31
        check_update_handler_(event_loop),
32
        inventory_update_handler_(event_loop),
33
        termination_handler_(event_loop),
34
        submit_inventory_state_(event_loop),
35
        poll_for_deployment_state_(event_loop),
36
        send_download_status_state_(deployments::DeploymentStatus::Downloading),
×
37
        send_install_status_state_(deployments::DeploymentStatus::Installing),
×
38
        send_reboot_status_state_(deployments::DeploymentStatus::Rebooting),
×
39
        send_commit_status_state_(
40
                deployments::DeploymentStatus::Installing,
×
41
                event_loop,
42
                ctx.mender_context.GetConfig().retry_poll_interval_seconds),
47✔
43
        // nullopt means: Fetch success/failure status from deployment context
44
        send_final_status_state_(
45
                optional::nullopt, event_loop, ctx.mender_context.GetConfig().retry_poll_interval_seconds),
47✔
46
        exit_state_(event_loop),
47
        main_states_(idle_state_),
48
        runner_(ctx) {
47✔
49
        runner_.AddStateMachine(main_states_);
47✔
50
        runner_.AddStateMachine(deployment_tracking_.states_);
47✔
51

52
        runner_.AttachToEventLoop(event_loop_);
47✔
53

54
        using se = StateEvent;
55
        using tf = sm::TransitionFlag;
56

57
        // When updating the table below, make sure that the initial states are in sync as well, in
58
        // LoadStateFromDb().
59

60
        // clang-format off
61
        main_states_.AddTransition(idle_state_,                          se::DeploymentPollingTriggered, poll_for_deployment_state_,           tf::Deferred );
47✔
62
        main_states_.AddTransition(idle_state_,                          se::InventoryPollingTriggered,  submit_inventory_state_,              tf::Deferred );
47✔
63

64
        main_states_.AddTransition(submit_inventory_state_,              se::Success,                    idle_state_,                          tf::Immediate);
47✔
65
        main_states_.AddTransition(submit_inventory_state_,              se::Failure,                    idle_state_,                          tf::Immediate);
47✔
66

67
        main_states_.AddTransition(poll_for_deployment_state_,           se::Success,                    send_download_status_state_,          tf::Immediate);
47✔
68
        main_states_.AddTransition(poll_for_deployment_state_,           se::NothingToDo,                idle_state_,                          tf::Immediate);
47✔
69
        main_states_.AddTransition(poll_for_deployment_state_,           se::Failure,                    idle_state_,                          tf::Immediate);
47✔
70

71
        // Cannot fail due to FailureMode::Ignore.
72
        main_states_.AddTransition(send_download_status_state_,          se::Success,                    update_download_state_,               tf::Immediate);
47✔
73

74
        main_states_.AddTransition(update_download_state_,               se::Success,                    send_install_status_state_,           tf::Immediate);
47✔
75
        main_states_.AddTransition(update_download_state_,               se::Failure,                    update_rollback_not_needed_state_,    tf::Immediate);
47✔
76
        // Empty payload
77
        main_states_.AddTransition(update_download_state_,               se::NothingToDo,                update_save_provides_state_,          tf::Immediate);
47✔
78
        main_states_.AddTransition(update_download_state_,               se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
79

80
        // Cannot fail due to FailureMode::Ignore.
81
        main_states_.AddTransition(send_install_status_state_,           se::Success,                    update_install_state_,                tf::Immediate);
47✔
82

83
        main_states_.AddTransition(update_install_state_,                se::Success,                    update_check_reboot_state_,           tf::Immediate);
47✔
84
        main_states_.AddTransition(update_install_state_,                se::Failure,                    update_check_rollback_state_,         tf::Immediate);
47✔
85
        main_states_.AddTransition(update_install_state_,                se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
86

87
        main_states_.AddTransition(update_check_reboot_state_,           se::Success,                    send_reboot_status_state_,            tf::Immediate);
47✔
88
        main_states_.AddTransition(update_check_reboot_state_,           se::NothingToDo,                update_before_commit_state_,          tf::Immediate);
47✔
89
        main_states_.AddTransition(update_check_reboot_state_,           se::Failure,                    update_check_rollback_state_,         tf::Immediate);
47✔
90
        main_states_.AddTransition(update_check_reboot_state_,           se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
91

92
        // Cannot fail due to FailureMode::Ignore.
93
        main_states_.AddTransition(send_reboot_status_state_,            se::Success,                    update_reboot_state_,                 tf::Immediate);
47✔
94

95
        main_states_.AddTransition(update_reboot_state_,                 se::Success,                    update_verify_reboot_state_,          tf::Immediate);
47✔
96
        main_states_.AddTransition(update_reboot_state_,                 se::Failure,                    update_check_rollback_state_,         tf::Immediate);
47✔
97
        main_states_.AddTransition(update_reboot_state_,                 se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
98

99
        main_states_.AddTransition(update_verify_reboot_state_,          se::Success,                    update_before_commit_state_,          tf::Immediate);
47✔
100
        main_states_.AddTransition(update_verify_reboot_state_,          se::Failure,                    update_check_rollback_state_,         tf::Immediate);
47✔
101
        main_states_.AddTransition(update_verify_reboot_state_,          se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
102

103
        // Cannot fail.
104
        main_states_.AddTransition(update_before_commit_state_,          se::Success,                    send_commit_status_state_,            tf::Immediate);
47✔
105

106
        main_states_.AddTransition(send_commit_status_state_,            se::Success,                    update_commit_state_,                 tf::Immediate);
47✔
107
        main_states_.AddTransition(send_commit_status_state_,            se::Failure,                    update_check_rollback_state_,         tf::Immediate);
47✔
108

109
        main_states_.AddTransition(update_commit_state_,                 se::Success,                    update_after_commit_state_,           tf::Immediate);
47✔
110
        main_states_.AddTransition(update_commit_state_,                 se::Failure,                    update_check_rollback_state_,         tf::Immediate);
47✔
111
        main_states_.AddTransition(update_commit_state_,                 se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
112

113
        main_states_.AddTransition(update_after_commit_state_,           se::Success,                    update_save_provides_state_,          tf::Immediate);
47✔
114
        main_states_.AddTransition(update_after_commit_state_,           se::Failure,                    update_save_provides_state_,          tf::Immediate);
47✔
115
        main_states_.AddTransition(update_after_commit_state_,           se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
116

117
        main_states_.AddTransition(update_check_rollback_state_,         se::Success,                    update_rollback_state_,               tf::Immediate);
47✔
118
        main_states_.AddTransition(update_check_rollback_state_,         se::NothingToDo,                update_failure_state_,                tf::Immediate);
47✔
119
        main_states_.AddTransition(update_check_rollback_state_,         se::Failure,                    update_failure_state_,                tf::Immediate);
47✔
120
        main_states_.AddTransition(update_check_rollback_state_,         se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
121

122
        main_states_.AddTransition(update_rollback_state_,               se::Success,                    update_check_rollback_reboot_state_,  tf::Immediate);
47✔
123
        main_states_.AddTransition(update_rollback_state_,               se::Failure,                    update_failure_state_,                tf::Immediate);
47✔
124
        main_states_.AddTransition(update_rollback_state_,               se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
125

126
        main_states_.AddTransition(update_check_rollback_reboot_state_,  se::Success,                    update_rollback_reboot_state_,        tf::Immediate);
47✔
127
        main_states_.AddTransition(update_check_rollback_reboot_state_,  se::NothingToDo,                update_rollback_successful_state_,    tf::Immediate);
47✔
128
        main_states_.AddTransition(update_check_rollback_reboot_state_,  se::Failure,                    update_failure_state_,                tf::Immediate);
47✔
129
        main_states_.AddTransition(update_check_rollback_reboot_state_,  se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
130

131
        // No Failure transition for this state, see comments in handler.
132
        main_states_.AddTransition(update_rollback_reboot_state_,        se::Success,                    update_verify_rollback_reboot_state_, tf::Immediate);
47✔
133
        main_states_.AddTransition(update_rollback_reboot_state_,        se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
134

135
        main_states_.AddTransition(update_verify_rollback_reboot_state_, se::Success,                    update_rollback_successful_state_,    tf::Immediate);
47✔
136
        main_states_.AddTransition(update_verify_rollback_reboot_state_, se::Retry,                      update_rollback_reboot_state_,        tf::Immediate);
47✔
137
        main_states_.AddTransition(update_verify_rollback_reboot_state_, se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
138

139
        main_states_.AddTransition(update_rollback_successful_state_,    se::Success,                    update_failure_state_,                tf::Immediate);
47✔
140

141
        main_states_.AddTransition(update_failure_state_,                se::Success,                    update_save_provides_state_,          tf::Immediate);
47✔
142
        main_states_.AddTransition(update_failure_state_,                se::Failure,                    update_save_provides_state_,          tf::Immediate);
47✔
143
        main_states_.AddTransition(update_failure_state_,                se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
144

145
        main_states_.AddTransition(update_save_provides_state_,          se::Success,                    update_cleanup_state_,                tf::Immediate);
47✔
146
        // Even if this fails, there is nothing we can do at this point.
147
        main_states_.AddTransition(update_save_provides_state_,          se::Failure,                    update_cleanup_state_,                tf::Immediate);
47✔
148
        main_states_.AddTransition(update_save_provides_state_,          se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
149

150
        main_states_.AddTransition(update_rollback_not_needed_state_,    se::Success,                    update_cleanup_state_,                tf::Immediate);
47✔
151

152
        main_states_.AddTransition(update_cleanup_state_,                se::Success,                    send_final_status_state_,             tf::Immediate);
47✔
153
        main_states_.AddTransition(update_cleanup_state_,                se::Failure,                    send_final_status_state_,             tf::Immediate);
47✔
154
        main_states_.AddTransition(update_cleanup_state_,                se::StateLoopDetected,          state_loop_state_,                    tf::Immediate);
47✔
155

156
        main_states_.AddTransition(state_loop_state_,                    se::Success,                    send_final_status_state_,             tf::Immediate);
47✔
157
        main_states_.AddTransition(state_loop_state_,                    se::Failure,                    send_final_status_state_,             tf::Immediate);
47✔
158

159
        main_states_.AddTransition(send_final_status_state_,             se::Success,                    clear_artifact_data_state_,           tf::Immediate);
47✔
160
        main_states_.AddTransition(send_final_status_state_,             se::Failure,                    clear_artifact_data_state_,           tf::Immediate);
47✔
161

162
        main_states_.AddTransition(clear_artifact_data_state_,           se::Success,                    end_of_deployment_state_,             tf::Immediate);
47✔
163
        main_states_.AddTransition(clear_artifact_data_state_,           se::Failure,                    end_of_deployment_state_,             tf::Immediate);
47✔
164

165
        main_states_.AddTransition(end_of_deployment_state_,             se::Success,                    idle_state_,                          tf::Immediate);
47✔
166

167
        auto &dt = deployment_tracking_;
47✔
168

169
        dt.states_.AddTransition(dt.idle_state_,                         se::DeploymentStarted,          dt.no_failures_state_,                tf::Immediate);
47✔
170

171
        dt.states_.AddTransition(dt.no_failures_state_,                  se::Failure,                    dt.failure_state_,                    tf::Immediate);
47✔
172
        dt.states_.AddTransition(dt.no_failures_state_,                  se::DeploymentEnded,            dt.idle_state_,                       tf::Immediate);
47✔
173

174
        dt.states_.AddTransition(dt.failure_state_,                      se::RollbackStarted,            dt.rollback_attempted_state_,         tf::Immediate);
47✔
175
        dt.states_.AddTransition(dt.failure_state_,                      se::DeploymentEnded,            dt.idle_state_,                       tf::Immediate);
47✔
176

177
        dt.states_.AddTransition(dt.rollback_attempted_state_,           se::Failure,                    dt.rollback_failed_state_,            tf::Immediate);
47✔
178
        dt.states_.AddTransition(dt.rollback_attempted_state_,           se::DeploymentEnded,            dt.idle_state_,                       tf::Immediate);
47✔
179

180
        dt.states_.AddTransition(dt.rollback_failed_state_,              se::DeploymentEnded,            dt.idle_state_,                       tf::Immediate);
47✔
181
        // clang-format on
182
}
47✔
183

184
StateMachine::StateMachine(
44✔
185
        Context &ctx, events::EventLoop &event_loop, chrono::milliseconds minimum_wait_time) :
44✔
186
        StateMachine(ctx, event_loop) {
44✔
187
        send_commit_status_state_.SetSmallestWaitInterval(minimum_wait_time);
44✔
188
        send_final_status_state_.SetSmallestWaitInterval(minimum_wait_time);
44✔
189
}
44✔
190

191
StateMachine::DeploymentTracking::DeploymentTracking() :
47✔
192
        states_(idle_state_) {
47✔
193
}
47✔
194

195
void StateMachine::LoadStateFromDb() {
44✔
196
        unique_ptr<StateData> state_data(new StateData);
44✔
197
        auto exp_loaded = ctx_.LoadDeploymentStateData(*state_data);
44✔
198
        if (!exp_loaded) {
44✔
199
                if (exp_loaded.error().code
4✔
200
                        == context::MakeError(context::StateDataStoreCountExceededError, "").code) {
4✔
201
                        log::Error("State loop detected. Forcefully aborting update.");
1✔
202

203
                        // This particular error code also fills in state_data.
204
                        ctx_.deployment.state_data = std::move(state_data);
1✔
205

206
                        ctx_.BeginDeploymentLogging();
1✔
207

208
                        main_states_.SetState(state_loop_state_);
1✔
209
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_failed_state_);
1✔
210
                } else {
211
                        log::Error(
1✔
212
                                "Unable to load deployment data from database: " + exp_loaded.error().String());
2✔
213
                        log::Error("Starting from initial state");
1✔
214
                }
215
                return;
2✔
216
        }
217

218
        if (!exp_loaded.value()) {
42✔
219
                log::Debug("No existing deployment data, starting from initial state");
27✔
220
                return;
27✔
221
        }
222

223
        // We have state data, move it to the context.
224
        ctx_.deployment.state_data = std::move(state_data);
15✔
225

226
        ctx_.BeginDeploymentLogging();
15✔
227

228
        auto &state = ctx_.deployment.state_data->state;
15✔
229

230
        if (state == ctx_.kUpdateStateDownload) {
15✔
231
                main_states_.SetState(update_cleanup_state_);
1✔
232
                // "rollback_attempted_state" because Download in its nature makes no system
233
                // changes, so a rollback is a no-op.
234
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
1✔
235

236
        } else if (state == ctx_.kUpdateStateArtifactReboot) {
14✔
237
                // Normal update path with a reboot.
238
                main_states_.SetState(update_verify_reboot_state_);
2✔
239
                deployment_tracking_.states_.SetState(deployment_tracking_.no_failures_state_);
2✔
240

241
        } else if (state == ctx_.kUpdateStateArtifactRollback) {
12✔
242
                // Installation failed, but rollback could still succeed.
243
                main_states_.SetState(update_rollback_state_);
1✔
244
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
1✔
245

246
        } else if (
11✔
247
                state == ctx_.kUpdateStateArtifactRollbackReboot
11✔
248
                || state == ctx_.kUpdateStateArtifactVerifyRollbackReboot
10✔
249
                || state == ctx_.kUpdateStateVerifyRollbackReboot) {
21✔
250
                // Normal flow for a rebooting rollback.
251
                main_states_.SetState(update_verify_rollback_reboot_state_);
2✔
252
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
2✔
253

254
        } else if (
9✔
255
                state == ctx_.kUpdateStateAfterArtifactCommit
9✔
256
                || state == ctx_.kUpdateStateUpdateAfterFirstCommit) {
9✔
257
                // Re-run commit Leave scripts if spontaneously rebooted after commit.
258
                main_states_.SetState(update_after_commit_state_);
×
259
                deployment_tracking_.states_.SetState(deployment_tracking_.no_failures_state_);
×
260

261
        } else if (state == ctx_.kUpdateStateArtifactFailure) {
9✔
262
                // Re-run ArtifactFailure if spontaneously rebooted before finishing.
263
                main_states_.SetState(update_failure_state_);
2✔
264
                if (ctx_.deployment.state_data->update_info.all_rollbacks_successful) {
2✔
265
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
1✔
266
                } else {
267
                        deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
1✔
268
                }
269

270
        } else if (state == ctx_.kUpdateStateCleanup) {
7✔
271
                // Re-run Cleanup if spontaneously rebooted before finishing.
272
                main_states_.SetState(update_cleanup_state_);
2✔
273
                if (ctx_.deployment.state_data->update_info.all_rollbacks_successful) {
2✔
274
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
1✔
275
                } else {
276
                        deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
1✔
277
                }
278

279
        } else {
280
                // All other states trigger a rollback.
281
                main_states_.SetState(update_check_rollback_state_);
5✔
282
                deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
5✔
283
        }
284

285
        auto &payload_types = ctx_.deployment.state_data->update_info.artifact.payload_types;
15✔
286
        assert(payload_types.size() == 1);
15✔
287
        ctx_.deployment.update_module.reset(
15✔
288
                new update_module::UpdateModule(ctx_.mender_context, payload_types[0]));
15✔
289
}
290

291
error::Error StateMachine::Run() {
47✔
292
        // Client is supposed to do one handling of each on startup.
293
        runner_.PostEvent(StateEvent::InventoryPollingTriggered);
47✔
294
        runner_.PostEvent(StateEvent::DeploymentPollingTriggered);
47✔
295

296
        auto err = RegisterSignalHandlers();
94✔
297
        if (err != error::NoError) {
47✔
298
                return err;
×
299
        }
300

301
        log::Info("Running Mender client " + conf::kMenderVersion);
47✔
302

303
        event_loop_.Run();
47✔
304
        return exit_state_.exit_error;
47✔
305
}
306

307
void StateMachine::StopAfterDeployment() {
46✔
308
        main_states_.AddTransition(
46✔
309
                end_of_deployment_state_,
310
                StateEvent::DeploymentEnded,
311
                exit_state_,
312
                sm::TransitionFlag::Immediate);
313
}
46✔
314

315
} // namespace daemon
316
} // namespace update
317
} // 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