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

mendersoftware / mender / 1996483152

21 Aug 2025 01:44PM UTC coverage: 75.933%. Remained the same
1996483152

push

gitlab-ci

danielskinstad
fix: add explicit check and handling for aborted deployments

Ticket: ME-527
Changelog: Fix an issue where a user-aborted deployment would reboot and rollback
during e.g. download. This is fixed by adding a new state event, `DeploymentAborted`,
which is posted when sending status updates to the server. Each state that sends
deployment status now explicitly checks if the deployment is aborted and handles
it accordingly.

Signed-off-by: Daniel Skinstad Drabitzius <daniel.drabitzius@northern.tech>

9 of 10 new or added lines in 3 files covered. (90.0%)

18 existing lines in 2 files now uncovered.

7383 of 9723 relevant lines covered (75.93%)

13940.99 hits per line

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

95.86
/src/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 <mender-update/daemon/state_machine.hpp>
16

17
#include <client_shared/conf.hpp>
18
#include <common/key_value_database.hpp>
19
#include <common/log.hpp>
20

21
#include <mender-update/daemon/states.hpp>
22

23
namespace mender {
24
namespace update {
25
namespace daemon {
26

27
namespace conf = mender::client_shared::conf;
28
namespace kvdb = mender::common::key_value_database;
29
namespace log = mender::common::log;
30

31
StateMachine::StateMachine(Context &ctx, events::EventLoop &event_loop) :
96✔
32
        ctx_(ctx),
33
        event_loop_(event_loop),
34
        check_update_handler_(event_loop),
35
        inventory_update_handler_(event_loop),
36
        termination_handler_(event_loop),
37
        schedule_submit_inventory_state_(
38
                ctx.inventory_timer,
96✔
39
                "inventory submission",
40
                StateEvent::InventoryPollingTriggered,
41
                ctx.mender_context.GetConfig().inventory_poll_interval_seconds),
96✔
42
        schedule_poll_for_deployment_state_(
43
                ctx.deployment_timer,
96✔
44
                "deployment check",
45
                StateEvent::DeploymentPollingTriggered,
46
                ctx.mender_context.GetConfig().update_poll_interval_seconds),
96✔
47
        submit_inventory_state_(
48
                ctx.mender_context.GetConfig().retry_poll_interval_seconds,
49
                ctx.mender_context.GetConfig().retry_poll_count),
96✔
50
        poll_for_deployment_state_(
51
                ctx.mender_context.GetConfig().retry_poll_interval_seconds,
52
                ctx.mender_context.GetConfig().retry_poll_count),
96✔
53
        send_download_status_state_(deployments::DeploymentStatus::Downloading),
54
        send_install_status_state_(deployments::DeploymentStatus::Installing),
55
        send_reboot_status_state_(deployments::DeploymentStatus::Rebooting),
56
        send_commit_status_state_(
57
                deployments::DeploymentStatus::Installing,
58
                event_loop,
59
                ctx.mender_context.GetConfig().retry_poll_interval_seconds,
60
                ctx.mender_context.GetConfig().retry_poll_count),
96✔
61
        // nullopt means: Fetch success/failure status from deployment context
62
        send_final_status_state_(
63
                nullopt,
64
                event_loop,
65
                ctx.mender_context.GetConfig().retry_poll_interval_seconds,
66
                ctx.mender_context.GetConfig().retry_poll_count),
96✔
67
        exit_state_(event_loop),
68
        main_states_(init_state_),
69
        state_scripts_(
70
                event_loop,
71
                chrono::seconds {ctx.mender_context.GetConfig().state_script_timeout_seconds},
72
                chrono::seconds {ctx.mender_context.GetConfig().state_script_retry_interval_seconds},
73
                chrono::seconds {ctx.mender_context.GetConfig().state_script_retry_timeout_seconds},
96✔
74
                ctx_.mender_context.GetConfig().paths.GetArtScriptsPath(),
192✔
75
                ctx_.mender_context.GetConfig().paths.GetRootfsScriptsPath()),
192✔
76
        runner_(ctx) {
480✔
77
        runner_.AddStateMachine(deployment_tracking_.states_);
96✔
78
        runner_.AddStateMachine(main_states_);
96✔
79
        runner_.AttachToEventLoop(event_loop_);
96✔
80
        ctx.authenticator.RegisterTokenReceivedCallback([&ctx]() {
×
81
                if (ctx.inventory_client->has_submitted_inventory) {
×
82
                        log::Debug("Client has re-authenticated - clear inventory data cache");
×
83
                        ctx.inventory_client->ClearDataCache();
×
84
                        ctx.inventory_client->has_submitted_inventory = false;
×
85
                }
86
        });
×
87

88
        using se = StateEvent;
89
        using tf = sm::TransitionFlag;
90

91
        auto &ss = state_scripts_;
92

93
        // When updating the table below, make sure that the initial states are in sync as well, in
94
        // LoadStateFromDb().
95

96
        // clang-format off
97
        main_states_.AddTransition(init_state_,                             se::Started,                     ss.idle_enter_,                          tf::Immediate);
96✔
98

99
        main_states_.AddTransition(ss.idle_enter_,                          se::Success,                     idle_state_,                             tf::Immediate);
96✔
100
        main_states_.AddTransition(ss.idle_enter_,                          se::Failure,                     idle_state_,                             tf::Immediate);
96✔
101

102
        main_states_.AddTransition(idle_state_,                             se::DeploymentPollingTriggered,  schedule_poll_for_deployment_state_,     tf::Deferred);
96✔
103
        main_states_.AddTransition(idle_state_,                             se::InventoryPollingTriggered,   schedule_submit_inventory_state_,        tf::Deferred);
96✔
104

105
        // The schedule states cannot fail
106
        main_states_.AddTransition(schedule_poll_for_deployment_state_,     se::Success,                     ss.idle_leave_deploy_,                   tf::Immediate);
96✔
107
        main_states_.AddTransition(schedule_submit_inventory_state_,        se::Success,                     ss.idle_leave_inv_,                      tf::Immediate);
96✔
108

109
        main_states_.AddTransition(ss.idle_leave_deploy_,                   se::Success,                     ss.sync_enter_deployment_,               tf::Immediate);
96✔
110
        main_states_.AddTransition(ss.idle_leave_deploy_,                   se::Failure,                     ss.sync_enter_deployment_,               tf::Immediate);
96✔
111

112
        main_states_.AddTransition(ss.sync_enter_deployment_,               se::Success,                     poll_for_deployment_state_,              tf::Immediate);
96✔
113
        main_states_.AddTransition(ss.sync_enter_deployment_,               se::Failure,                     ss.sync_error_,                          tf::Immediate);
96✔
114

115
        main_states_.AddTransition(ss.sync_error_,                          se::Success,                     ss.idle_enter_,                          tf::Immediate);
96✔
116
        main_states_.AddTransition(ss.sync_error_,                          se::Failure,                     ss.idle_enter_,                          tf::Immediate);
96✔
117

118
        main_states_.AddTransition(ss.idle_leave_inv_,                      se::Success,                     ss.sync_enter_inventory_,                tf::Immediate);
96✔
119
        main_states_.AddTransition(ss.idle_leave_inv_,                      se::Failure,                     ss.sync_enter_inventory_,                tf::Immediate);
96✔
120

121
        main_states_.AddTransition(ss.sync_enter_inventory_,                se::Success,                     submit_inventory_state_,                 tf::Immediate);
96✔
122
        main_states_.AddTransition(ss.sync_enter_inventory_,                se::Failure,                     ss.sync_error_,                          tf::Immediate);
96✔
123

124
        main_states_.AddTransition(submit_inventory_state_,                 se::Success,                     ss.sync_leave_,                          tf::Immediate);
96✔
125
        main_states_.AddTransition(submit_inventory_state_,                 se::Failure,                     ss.sync_error_,                          tf::Immediate);
96✔
126

127
        main_states_.AddTransition(poll_for_deployment_state_,              se::Success,                     ss.sync_leave_download_,                 tf::Immediate);
96✔
128
        main_states_.AddTransition(poll_for_deployment_state_,              se::NothingToDo,                 ss.sync_leave_,                          tf::Immediate);
96✔
129
        main_states_.AddTransition(poll_for_deployment_state_,              se::Failure,                     ss.sync_error_,                          tf::Immediate);
96✔
130

131
        main_states_.AddTransition(ss.sync_leave_,                          se::Success,                     ss.idle_enter_,                          tf::Immediate);
96✔
132
        main_states_.AddTransition(ss.sync_leave_,                          se::Failure,                     ss.sync_error_,                          tf::Immediate);
96✔
133

134
        main_states_.AddTransition(ss.sync_leave_download_,                 se::Success,                     send_download_status_state_,             tf::Immediate);
96✔
135
        main_states_.AddTransition(ss.sync_leave_download_,                 se::Failure,                     ss.sync_error_download_,                 tf::Immediate);
96✔
136

137
        main_states_.AddTransition(ss.sync_error_download_,                 se::Success,                     end_of_deployment_state_,                tf::Immediate);
96✔
138
        main_states_.AddTransition(ss.sync_error_download_,                 se::Failure,                     end_of_deployment_state_,                tf::Immediate);
96✔
139

140
        // Fail the deployment if it's aborted. All other failures will be ignored due to FailureMode::Ignore
141
        main_states_.AddTransition(send_download_status_state_,             se::Success,                     ss.download_enter_,                      tf::Immediate);
96✔
142
        main_states_.AddTransition(send_download_status_state_,             se::DeploymentAborted,           update_cleanup_state_,                   tf::Immediate);
96✔
143

144
        main_states_.AddTransition(ss.download_enter_,                      se::Success,                     update_download_state_,                  tf::Immediate);
96✔
145
        main_states_.AddTransition(ss.download_enter_,                      se::Failure,                     ss.download_error_,                      tf::Immediate);
96✔
146
        main_states_.AddTransition(ss.download_enter_,                      se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
147
        main_states_.AddTransition(ss.download_error_,                      se::Success,                     update_rollback_not_needed_state_,       tf::Immediate);
96✔
148
        main_states_.AddTransition(ss.download_error_,                      se::Failure,                     update_rollback_not_needed_state_,       tf::Immediate);
96✔
149

150
        main_states_.AddTransition(update_download_state_,                  se::Success,                     ss.download_leave_,                      tf::Immediate);
96✔
151
        main_states_.AddTransition(update_download_state_,                  se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
152
        main_states_.AddTransition(update_download_state_,                  se::Failure,                     update_download_cancel_state_,           tf::Immediate);
96✔
153
        main_states_.AddTransition(update_download_state_,                  se::NothingToDo,                 ss.download_leave_save_provides,         tf::Immediate);
96✔
154

155
        // Cannot fail because download cancellation is a void function as there's nothing to do if it fails, anyway.
156
        main_states_.AddTransition(update_download_cancel_state_,           se::Success,                     ss.download_error_,                      tf::Immediate);
96✔
157

158
        main_states_.AddTransition(ss.download_leave_,                      se::Success,                     send_install_status_state_,              tf::Immediate);
96✔
159
        main_states_.AddTransition(ss.download_leave_,                      se::Failure,                     ss.download_error_,                      tf::Immediate);
96✔
160

161
        main_states_.AddTransition(ss.download_leave_save_provides,         se::Success,                     update_save_provides_state_,             tf::Immediate);
96✔
162
        main_states_.AddTransition(ss.download_leave_save_provides,         se::Failure,                     ss.download_error_,                      tf::Immediate);
96✔
163

164
        main_states_.AddTransition(ss.install_enter_,                       se::Success,                     update_install_state_,                   tf::Immediate);
96✔
165
        main_states_.AddTransition(ss.install_enter_,                       se::Failure,                     ss.install_error_rollback_,              tf::Immediate);
96✔
166

167
        // Fail the deployment if it's aborted. All other failures will be ignored due to FailureMode::Ignore
168
        main_states_.AddTransition(send_install_status_state_,              se::Success,                     ss.install_enter_,                       tf::Immediate);
96✔
169
        main_states_.AddTransition(send_install_status_state_,              se::DeploymentAborted,           update_cleanup_state_,                   tf::Immediate);
96✔
170

171
        main_states_.AddTransition(update_install_state_,                   se::Success,                     ss.install_leave_,                       tf::Immediate);
96✔
172
        main_states_.AddTransition(update_install_state_,                   se::Failure,                     ss.install_error_rollback_,              tf::Immediate);
96✔
173
        main_states_.AddTransition(update_install_state_,                   se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
174

175
        main_states_.AddTransition(ss.install_leave_,                       se::Success,                     update_check_reboot_state_,              tf::Immediate);
96✔
176
        main_states_.AddTransition(ss.install_leave_,                       se::Failure,                     ss.install_error_rollback_,              tf::Immediate);
96✔
177
        main_states_.AddTransition(ss.install_error_rollback_,              se::Success,                     update_check_rollback_state_,            tf::Immediate);
96✔
178
        main_states_.AddTransition(ss.install_error_rollback_,              se::Failure,                     update_check_rollback_state_,            tf::Immediate);
96✔
179

180
        main_states_.AddTransition(ss.failure_enter_,                       se::Success,                     update_failure_state_,                   tf::Immediate);
96✔
181
        main_states_.AddTransition(ss.failure_enter_,                       se::Failure,                     update_failure_state_,                   tf::Immediate);
96✔
182
        main_states_.AddTransition(ss.failure_enter_,                       se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
183

184

185
        main_states_.AddTransition(update_check_reboot_state_,              se::Success,                     send_reboot_status_state_,               tf::Immediate);
96✔
186
        main_states_.AddTransition(update_check_reboot_state_,              se::NothingToDo,                 update_before_commit_state_,             tf::Immediate);
96✔
187
        main_states_.AddTransition(update_check_reboot_state_,              se::Failure,                     update_check_rollback_state_,            tf::Immediate);
96✔
188
        main_states_.AddTransition(update_check_reboot_state_,              se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
189

190
        // Fail the deployment if it's aborted. All other failures will be ignored due to FailureMode::Ignore
191
        main_states_.AddTransition(send_reboot_status_state_,               se::Success,                     ss.reboot_enter_,                        tf::Immediate);
96✔
192
        main_states_.AddTransition(send_reboot_status_state_,               se::DeploymentAborted,           update_check_rollback_state_,            tf::Immediate);
96✔
193

194
        main_states_.AddTransition(ss.reboot_enter_,                        se::Success,                     update_reboot_state_,                    tf::Immediate);
96✔
195
        main_states_.AddTransition(ss.reboot_enter_,                        se::Failure,                     ss.reboot_error_,                        tf::Immediate);
96✔
196

197
        main_states_.AddTransition(update_reboot_state_,                    se::Success,                     update_verify_reboot_state_,             tf::Immediate);
96✔
198
        main_states_.AddTransition(update_reboot_state_,                    se::Failure,                     ss.reboot_error_,                        tf::Immediate);
96✔
199
        main_states_.AddTransition(update_reboot_state_,                    se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
200

201
        main_states_.AddTransition(ss.reboot_error_,                        se::Success,                     update_check_rollback_state_,            tf::Immediate);
96✔
202
        main_states_.AddTransition(ss.reboot_error_,                        se::Failure,                     update_check_rollback_state_,            tf::Immediate);
96✔
203

204
        main_states_.AddTransition(update_verify_reboot_state_,             se::Success,                     ss.reboot_leave_,                        tf::Immediate);
96✔
205
        main_states_.AddTransition(update_verify_reboot_state_,             se::Failure,                     ss.reboot_error_,                        tf::Immediate);
96✔
206
        main_states_.AddTransition(update_verify_reboot_state_,             se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
207

208
        main_states_.AddTransition(ss.reboot_leave_,                        se::Success,                     update_before_commit_state_,             tf::Immediate);
96✔
209
        main_states_.AddTransition(ss.reboot_leave_,                        se::Failure,                     ss.reboot_error_,                        tf::Immediate);
96✔
210

211
        // Cannot fail.
212
        main_states_.AddTransition(update_before_commit_state_,             se::Success,                     send_commit_status_state_,               tf::Immediate);
96✔
213

214
        // From here on out we treat any failure (including DeploymentAborted) the same way
215
        main_states_.AddTransition(send_commit_status_state_,               se::Success,                     ss.commit_enter_,                        tf::Immediate);
96✔
216
        main_states_.AddTransition(send_commit_status_state_,               se::Failure,                     update_check_rollback_state_,            tf::Immediate);
96✔
217
        main_states_.AddTransition(send_commit_status_state_,               se::DeploymentAborted,           update_check_rollback_state_,            tf::Immediate);
96✔
218

219
        main_states_.AddTransition(ss.commit_enter_,                        se::Success,                     update_commit_state_,                    tf::Immediate);
96✔
220
        main_states_.AddTransition(ss.commit_enter_,                        se::Failure,                     ss.commit_error_,                        tf::Immediate);
96✔
221

222
        main_states_.AddTransition(ss.commit_error_,                        se::Success,                     update_check_rollback_state_,            tf::Immediate);
96✔
223
        main_states_.AddTransition(ss.commit_error_,                        se::Failure,                     update_check_rollback_state_,            tf::Immediate);
96✔
224

225
        main_states_.AddTransition(update_commit_state_,                    se::Success,                     update_after_commit_state_,              tf::Immediate);
96✔
226
        main_states_.AddTransition(update_commit_state_,                    se::Failure,                     ss.commit_error_,                        tf::Immediate);
96✔
227
        main_states_.AddTransition(update_commit_state_,                    se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
228

229
        main_states_.AddTransition(update_after_commit_state_,              se::Success,                     ss.commit_leave_,                        tf::Immediate);
96✔
230
        main_states_.AddTransition(update_after_commit_state_,              se::Failure,                     ss.commit_error_save_provides_,          tf::Immediate);
96✔
231
        main_states_.AddTransition(update_after_commit_state_,              se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
232

233
        main_states_.AddTransition(ss.commit_leave_,                        se::Success,                     update_save_provides_state_,             tf::Immediate);
96✔
234
        main_states_.AddTransition(ss.commit_leave_,                        se::Failure,                     ss.commit_error_save_provides_,          tf::Immediate);
96✔
235

236
        main_states_.AddTransition(ss.commit_error_save_provides_,          se::Success,                     update_save_provides_state_,             tf::Immediate);
96✔
237
        main_states_.AddTransition(ss.commit_error_save_provides_,          se::Failure,                     update_save_provides_state_,             tf::Immediate);
96✔
238

239
        main_states_.AddTransition(update_check_rollback_state_,            se::Success,                     ss.rollback_enter_,                      tf::Immediate);
96✔
240
        main_states_.AddTransition(update_check_rollback_state_,            se::NothingToDo,                 ss.failure_enter_,                       tf::Immediate);
96✔
241
        main_states_.AddTransition(update_check_rollback_state_,            se::Failure,                     ss.failure_enter_,                       tf::Immediate);
96✔
242
        main_states_.AddTransition(update_check_rollback_state_,            se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
243

244
        main_states_.AddTransition(ss.rollback_enter_,                      se::Success,                     update_rollback_state_,                  tf::Immediate);
96✔
245
        main_states_.AddTransition(ss.rollback_enter_,                      se::Failure,                     update_rollback_state_,                  tf::Immediate);
96✔
246

247
        main_states_.AddTransition(update_rollback_state_,                  se::Success,                     ss.rollback_leave_,                      tf::Immediate);
96✔
248
        main_states_.AddTransition(update_rollback_state_,                  se::Failure,                     ss.rollback_leave_error_,                tf::Immediate);
96✔
249
        main_states_.AddTransition(update_rollback_state_,                  se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
250

251
        main_states_.AddTransition(ss.rollback_leave_,                      se::Success,                     update_check_rollback_reboot_state_,     tf::Immediate);
96✔
252
        main_states_.AddTransition(ss.rollback_leave_,                      se::Failure,                     update_check_rollback_reboot_state_,     tf::Immediate);
96✔
253

254
        main_states_.AddTransition(ss.rollback_leave_error_,                se::Success,                     ss.failure_enter_,                       tf::Immediate);
96✔
255
        main_states_.AddTransition(ss.rollback_leave_error_,                se::Failure,                     ss.failure_enter_,                       tf::Immediate);
96✔
256

257
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::Success,                     ss.rollback_reboot_enter_,               tf::Immediate);
96✔
258
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::NothingToDo,                 update_rollback_successful_state_,       tf::Immediate);
96✔
259
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::Failure,                     ss.failure_enter_,                       tf::Immediate);
96✔
260
        main_states_.AddTransition(update_check_rollback_reboot_state_,     se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
261

262
        main_states_.AddTransition(ss.rollback_reboot_enter_,               se::Success,                     update_rollback_reboot_state_,           tf::Immediate);
96✔
263
        main_states_.AddTransition(ss.rollback_reboot_enter_,               se::Failure,                     update_rollback_reboot_state_,           tf::Immediate);
96✔
264

265
        main_states_.AddTransition(ss.rollback_reboot_error_,               se::Success,                     ss.failure_enter_,                       tf::Immediate);
96✔
266
        main_states_.AddTransition(ss.rollback_reboot_error_,               se::Failure,                     ss.failure_enter_,                       tf::Immediate);
96✔
267

268
        // No Failure transition for this state see comments in handler.
269
        main_states_.AddTransition(update_rollback_reboot_state_,           se::Success,                     update_verify_rollback_reboot_state_,    tf::Immediate);
96✔
270
        main_states_.AddTransition(update_rollback_reboot_state_,           se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
271

272
        main_states_.AddTransition(update_verify_rollback_reboot_state_,    se::Success,                     ss.rollback_reboot_leave_,               tf::Immediate);
96✔
273
        main_states_.AddTransition(update_verify_rollback_reboot_state_,    se::Retry,                       update_rollback_reboot_state_,           tf::Immediate);
96✔
274
        main_states_.AddTransition(update_verify_rollback_reboot_state_,    se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
275

276
        main_states_.AddTransition(ss.rollback_reboot_leave_,               se::Success,                     update_rollback_successful_state_,       tf::Immediate);
96✔
277
        main_states_.AddTransition(ss.rollback_reboot_leave_,               se::Failure,                     ss.rollback_reboot_error_,               tf::Immediate);
96✔
278

279
        main_states_.AddTransition(update_rollback_successful_state_,       se::Success,                     ss.failure_enter_,                       tf::Immediate);
96✔
280

281
        main_states_.AddTransition(update_failure_state_,                   se::Success,                     ss.failure_leave_update_save_provides_,  tf::Immediate);
96✔
282
        main_states_.AddTransition(update_failure_state_,                   se::Failure,                     ss.failure_leave_update_save_provides_,  tf::Immediate);
96✔
283
        main_states_.AddTransition(update_failure_state_,                   se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
284

285
        main_states_.AddTransition(ss.failure_leave_update_save_provides_,  se::Success,                     update_save_provides_state_,             tf::Immediate);
96✔
286
        main_states_.AddTransition(ss.failure_leave_update_save_provides_,  se::Failure,                     update_save_provides_state_,             tf::Immediate);
96✔
287

288
        // Even if this fails there is nothing we can do at this point.
289
        main_states_.AddTransition(update_save_provides_state_,             se::Success,                     update_cleanup_state_,                   tf::Immediate);
96✔
290
        main_states_.AddTransition(update_save_provides_state_,             se::Failure,                     update_cleanup_state_,                   tf::Immediate);
96✔
291
        main_states_.AddTransition(update_save_provides_state_,             se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
292

293
        main_states_.AddTransition(update_rollback_not_needed_state_,       se::Success,                     update_cleanup_state_,                   tf::Immediate);
96✔
294

295
        main_states_.AddTransition(update_cleanup_state_,                   se::Success,                     send_final_status_state_,                tf::Immediate);
96✔
296
        main_states_.AddTransition(update_cleanup_state_,                   se::Failure,                     send_final_status_state_,                tf::Immediate);
96✔
297
        main_states_.AddTransition(update_cleanup_state_,                   se::StateLoopDetected,           state_loop_state_,                       tf::Immediate);
96✔
298

299
        main_states_.AddTransition(state_loop_state_,                       se::Success,                     send_final_status_state_,                tf::Immediate);
96✔
300
        main_states_.AddTransition(state_loop_state_,                       se::Failure,                     send_final_status_state_,                tf::Immediate);
96✔
301

302
        main_states_.AddTransition(send_final_status_state_,                se::Success,                     clear_artifact_data_state_,              tf::Immediate);
96✔
303
        main_states_.AddTransition(send_final_status_state_,                se::Failure,                     clear_artifact_data_state_,              tf::Immediate);
96✔
304
        main_states_.AddTransition(send_final_status_state_,                se::DeploymentAborted,           clear_artifact_data_state_,              tf::Immediate);
96✔
305

306
        main_states_.AddTransition(clear_artifact_data_state_,              se::Success,                     end_of_deployment_state_,                tf::Immediate);
96✔
307
        main_states_.AddTransition(clear_artifact_data_state_,              se::Failure,                     end_of_deployment_state_,                tf::Immediate);
96✔
308

309
        main_states_.AddTransition(end_of_deployment_state_,                se::Success,                     ss.idle_enter_,                          tf::Immediate);
96✔
310

311
        auto &dt = deployment_tracking_;
312

313
        dt.states_.AddTransition(dt.idle_state_,                            se::DeploymentStarted,           dt.no_failures_state_,                   tf::Immediate);
96✔
314

315
        dt.states_.AddTransition(dt.no_failures_state_,                     se::Failure,                     dt.failure_state_,                       tf::Immediate);
96✔
316
        dt.states_.AddTransition(dt.no_failures_state_,                     se::DeploymentAborted,           dt.failure_state_,                       tf::Immediate);
96✔
317
        dt.states_.AddTransition(dt.no_failures_state_,                     se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
96✔
318

319
        dt.states_.AddTransition(dt.failure_state_,                         se::RollbackStarted,             dt.rollback_attempted_state_,            tf::Immediate);
96✔
320
        dt.states_.AddTransition(dt.failure_state_,                         se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
96✔
321

322
        dt.states_.AddTransition(dt.rollback_attempted_state_,              se::Failure,                     dt.rollback_failed_state_,               tf::Immediate);
96✔
323
        dt.states_.AddTransition(dt.rollback_attempted_state_,              se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
96✔
324

325
        dt.states_.AddTransition(dt.rollback_failed_state_,                 se::DeploymentEnded,             dt.idle_state_,                          tf::Immediate);
96✔
326
        // clang-format on
327
}
96✔
328

329
StateMachine::StateMachine(
91✔
330
        Context &ctx, events::EventLoop &event_loop, chrono::milliseconds minimum_wait_time) :
91✔
331
        StateMachine(ctx, event_loop) {
91✔
332
        send_commit_status_state_.SetSmallestWaitInterval(minimum_wait_time);
91✔
333
        send_final_status_state_.SetSmallestWaitInterval(minimum_wait_time);
91✔
334
}
91✔
335

336
StateMachine::DeploymentTracking::DeploymentTracking() :
96✔
337
        states_(idle_state_) {
96✔
338
}
96✔
339

340
void StateMachine::LoadStateFromDb() {
92✔
341
        unique_ptr<StateData> state_data(new StateData);
126✔
342
        auto exp_loaded = ctx_.LoadDeploymentStateData(*state_data);
92✔
343
        if (!exp_loaded) {
92✔
344
                if (exp_loaded.error().code
2✔
345
                        == context::MakeError(context::StateDataStoreCountExceededError, "").code) {
2✔
346
                        log::Error("State loop detected. Forcefully aborting update.");
2✔
347

348
                        // This particular error code also fills in state_data.
349
                        ctx_.deployment.state_data = std::move(state_data);
1✔
350

351
                        ctx_.BeginDeploymentLogging();
1✔
352

353
                        main_states_.SetState(state_loop_state_);
1✔
354
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_failed_state_);
1✔
355
                } else {
356
                        log::Error(
1✔
357
                                "Unable to load deployment data from database: " + exp_loaded.error().String());
2✔
358
                        log::Error("Starting from initial state");
2✔
359
                }
360
                return;
2✔
361
        }
362

363
        auto &store = ctx_.mender_context.GetMenderStoreDB();
90✔
364

365
        if (!exp_loaded.value()) {
90✔
366
                log::Debug("No existing deployment data, starting from initial state");
110✔
367

368
                auto err = store.Remove(ctx_.mender_context.update_control_maps);
55✔
369
                if (err != error::NoError) {
55✔
UNCOV
370
                        log::Error(
×
UNCOV
371
                                "Error removing " + ctx_.mender_context.update_control_maps
×
UNCOV
372
                                + " key from database: " + err.String());
×
373
                        // Nothing we can do about it.
374
                }
375

376
                return;
377
        }
378

379
        // We have state data, move it to the context.
380
        ctx_.deployment.state_data = std::move(state_data);
35✔
381

382
        ctx_.BeginDeploymentLogging();
35✔
383

384
        bool update_control_enabled = false;
385
        auto exp_update_control_data = store.Read(ctx_.mender_context.update_control_maps);
35✔
386
        if (exp_update_control_data) {
35✔
387
                auto update_control_data = common::StringFromByteVector(exp_update_control_data.value());
2✔
388
                if (update_control_data != "" && update_control_data != "{}") {
2✔
389
                        update_control_enabled = true;
390
                }
391
        } else if (exp_update_control_data.error().code != kvdb::MakeError(kvdb::KeyError, "").code) {
33✔
UNCOV
392
                log::Error("Error while loading update control data from database");
×
393
                // Since we don't actually need it, continue anyway.
394
        }
395

396
        auto &state = ctx_.deployment.state_data->state;
35✔
397

398
        if (state == ctx_.kUpdateStateDownload) {
35✔
399
                main_states_.SetState(update_cleanup_state_);
3✔
400
                // "rollback_attempted_state" because Download in its nature makes no system
401
                // changes, so a rollback is a no-op.
402
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
3✔
403

404
        } else if (state == ctx_.kUpdateStateArtifactReboot) {
32✔
405
                // Normal update path with a reboot.
406
                if (update_control_enabled) {
5✔
407
                        log::Error(
1✔
408
                                "This deployment was done using Update Control, but this client does not support Update Control. Failing / rolling back deployment.");
2✔
409
                        main_states_.SetState(state_scripts_.reboot_error_);
1✔
410
                        deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
1✔
411
                } else {
412
                        main_states_.SetState(update_verify_reboot_state_);
4✔
413
                        deployment_tracking_.states_.SetState(deployment_tracking_.no_failures_state_);
4✔
414
                }
415

416
        } else if (state == ctx_.kUpdateStateArtifactRollback) {
27✔
417
                // Installation failed, but rollback could still succeed.
418
                main_states_.SetState(state_scripts_.rollback_enter_);
3✔
419
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
3✔
420

421
        } else if (
422
                state == ctx_.kUpdateStateArtifactRollbackReboot
24✔
423
                || state == ctx_.kUpdateStateArtifactVerifyRollbackReboot
22✔
424
                || state == ctx_.kUpdateStateVerifyRollbackReboot) {
44✔
425
                // Normal flow for a rebooting rollback.
426
                main_states_.SetState(update_verify_rollback_reboot_state_);
4✔
427
                deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
4✔
428

429
        } else if (
430
                state == ctx_.kUpdateStateAfterArtifactCommit
20✔
431
                || state == ctx_.kUpdateStateUpdateAfterFirstCommit) {
20✔
432
                // Re-run commit Leave scripts if spontaneously rebooted after commit.
433
                main_states_.SetState(update_after_commit_state_);
2✔
434
                deployment_tracking_.states_.SetState(deployment_tracking_.no_failures_state_);
2✔
435

436
        } else if (state == ctx_.kUpdateStateArtifactFailure) {
18✔
437
                // Re-run ArtifactFailure if spontaneously rebooted before finishing.
438
                main_states_.SetState(state_scripts_.failure_enter_);
4✔
439
                if (ctx_.deployment.state_data->update_info.all_rollbacks_successful) {
4✔
440
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
3✔
441
                } else {
442
                        deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
1✔
443
                }
444

445
        } else if (state == ctx_.kUpdateStateCleanup) {
14✔
446
                // Re-run Cleanup if spontaneously rebooted before finishing.
447
                main_states_.SetState(update_cleanup_state_);
2✔
448
                if (ctx_.deployment.state_data->update_info.all_rollbacks_successful) {
2✔
449
                        deployment_tracking_.states_.SetState(deployment_tracking_.rollback_attempted_state_);
1✔
450
                } else {
451
                        deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
1✔
452
                }
453

454
        } else {
455
                // All other states trigger a rollback.
456
                main_states_.SetState(update_check_rollback_state_);
12✔
457
                deployment_tracking_.states_.SetState(deployment_tracking_.failure_state_);
12✔
458
        }
459

460
        auto &payload_types = ctx_.deployment.state_data->update_info.artifact.payload_types;
35✔
461
        if (payload_types.size() == 0) {
35✔
462
                ctx_.deployment.update_module.reset();
463
                return;
464
        }
465
        assert(payload_types.size() == 1);
466
        ctx_.deployment.update_module.reset(
467
                new update_module::UpdateModule(ctx_.mender_context, payload_types[0]));
34✔
468
}
469

470
error::Error StateMachine::Run() {
95✔
471
        // Client is supposed to do one handling of each on startup.
472
        runner_.PostEvent(StateEvent::InventoryPollingTriggered);
95✔
473
        runner_.PostEvent(StateEvent::DeploymentPollingTriggered);
95✔
474

475
        auto err = RegisterSignalHandlers();
95✔
476
        if (err != error::NoError) {
95✔
UNCOV
477
                return err;
×
478
        }
479

480
        log::Info("Running Mender client " + conf::kMenderVersion);
95✔
481

482
        event_loop_.Run();
95✔
483
        return exit_state_.exit_error;
95✔
484
}
485

486
void StateMachine::StopAfterDeployment() {
93✔
487
        main_states_.AddTransition(
93✔
488
                end_of_deployment_state_,
489
                StateEvent::DeploymentEnded,
490
                exit_state_,
491
                sm::TransitionFlag::Immediate);
492
}
93✔
493

494
#ifndef NDEBUG
495
void StateMachine::StopAfterDeployments(int number) {
496
        exit_state_.ExitAfter(number);
497
        main_states_.AddTransition(
498
                end_of_deployment_state_, StateEvent::Success, exit_state_, sm::TransitionFlag::Immediate);
499
        main_states_.AddTransition(
500
                exit_state_,
501
                StateEvent::Success,
502
                state_scripts_.idle_enter_,
503
                sm::TransitionFlag::Immediate);
504
}
505
#endif
506

507
} // namespace daemon
508
} // namespace update
509
} // 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