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

mendersoftware / mender / 1066350345

09 Nov 2023 08:56AM UTC coverage: 80.217% (+0.08%) from 80.14%
1066350345

push

gitlab-ci

lluiscampos
fix: Resume downloads when the first header handler errors

This will cover the case for example when the host cannot temporary be
resolved or the handshake fails.

It is explicitly tested on an integration test.

Ticket: MEN-6671
Changelog: None

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

6 of 6 new or added lines in 1 file covered. (100.0%)

17 existing lines in 2 files now uncovered.

6958 of 8674 relevant lines covered (80.22%)

9279.1 hits per line

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

64.81
/src/mender-update/cli/actions.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/cli/actions.hpp>
16

17
#include <algorithm>
18
#include <iostream>
19
#include <string>
20

21
#include <artifact/config.hpp>
22

23
#include <common/common.hpp>
24
#include <common/error.hpp>
25
#include <common/events.hpp>
26
#include <common/expected.hpp>
27
#include <common/key_value_database.hpp>
28
#include <common/log.hpp>
29
#include <common/path.hpp>
30
#include <common/processes.hpp>
31

32
#include <mender-update/cli/cli.hpp>
33
#include <mender-update/daemon.hpp>
34
#include <mender-update/standalone.hpp>
35

36
namespace mender {
37
namespace update {
38
namespace cli {
39

40
namespace processes = mender::common::processes;
41
namespace conf = mender::common::conf;
42
namespace daemon = mender::update::daemon;
43
namespace database = mender::common::key_value_database;
44
namespace error = mender::common::error;
45
namespace events = mender::common::events;
46
namespace expected = mender::common::expected;
47
namespace kv_db = mender::common::key_value_database;
48
namespace log = mender::common::log;
49
namespace path = mender::common::path;
50
namespace standalone = mender::update::standalone;
51

52
static error::Error DoMaybeInstallBootstrapArtifact(context::MenderContext &main_context) {
47✔
53
        const string bootstrap_artifact_path {
54
                main_context.GetConfig().paths.GetBootstrapArtifactFile()};
55
        // Check if the DB is populated - then install conditionally
56
        auto &db = main_context.GetMenderStoreDB();
47✔
57
        auto exp_key = db.Read(main_context.artifact_name_key);
47✔
58
        if (exp_key) {
47✔
59
                // Key exists. Do nothing
60
                return error::NoError;
13✔
61
        }
62
        error::Error err = exp_key.error();
34✔
63
        if (err.code != kv_db::MakeError(kv_db::KeyError, "Key Not found").code) {
34✔
64
                return err;
×
65
        }
66

67
        // Key does not exist, install the bootstrap artifact if it exists
68
        if (!path::FileExists(bootstrap_artifact_path)) {
34✔
69
                log::Debug("No Bootstrap Artifact found at: " + bootstrap_artifact_path);
33✔
70
                error::Error err =
71
                        db.Write(main_context.artifact_name_key, common::ByteVectorFromString("unknown"));
33✔
72
                if (err != error::NoError) {
33✔
73
                        return err;
×
74
                }
75
                return error::NoError;
33✔
76
        }
77
        log::Info("Installing the bootstrap Artifact");
2✔
78
        auto result = standalone::Install(
79
                main_context, bootstrap_artifact_path, artifact::config::Signature::Skip);
1✔
80

81
        if (result.err != error::NoError) {
1✔
82
                error::Error err =
83
                        db.Write(main_context.artifact_name_key, common::ByteVectorFromString("unknown"));
×
84
                return result.err.FollowedBy(err).WithContext("Failed to install the bootstrap Artifact");
×
85
        }
86
        return error::NoError;
1✔
87
}
88

89
error::Error MaybeInstallBootstrapArtifact(context::MenderContext &main_context) {
47✔
90
        const string bootstrap_artifact_path {
91
                main_context.GetConfig().paths.GetBootstrapArtifactFile()};
92
        error::Error err = DoMaybeInstallBootstrapArtifact(main_context);
47✔
93

94
        // Unconditionally delete the bootstrap Artifact
95
        if (path::FileExists(bootstrap_artifact_path)) {
47✔
96
                error::Error delete_err = path::FileDelete(bootstrap_artifact_path);
1✔
97
                if (delete_err != error::NoError) {
1✔
98
                        return err.FollowedBy(
99
                                delete_err.WithContext("Failed to delete the bootstrap Artifact"));
×
100
                }
101
        }
102
        return err;
47✔
103
}
104

105
error::Error ShowArtifactAction::Execute(context::MenderContext &main_context) {
2✔
106
        auto exp_provides = main_context.LoadProvides();
2✔
107
        if (!exp_provides) {
2✔
108
                return exp_provides.error();
×
109
        }
110

111
        auto &provides = exp_provides.value();
2✔
112
        if (provides.count("artifact_name") == 0 || provides["artifact_name"] == "") {
6✔
113
                cout << "Unknown" << endl;
1✔
114
        } else {
115
                cout << provides["artifact_name"] << endl;
3✔
116
        }
117
        return error::NoError;
2✔
118
}
119

120
error::Error ShowProvidesAction::Execute(context::MenderContext &main_context) {
32✔
121
        auto exp_provides = main_context.LoadProvides();
32✔
122
        if (!exp_provides) {
32✔
123
                return exp_provides.error();
×
124
        }
125

126
        auto &provides = exp_provides.value();
32✔
127
        for (const auto &elem : provides) {
104✔
128
                cout << elem.first << "=" << elem.second << endl;
72✔
129
        }
130

131
        return error::NoError;
32✔
132
}
133

134
static error::Error ResultHandler(standalone::ResultAndError result) {
54✔
135
        switch (result.result) {
54✔
136
        case standalone::Result::InstalledAndCommitted:
137
        case standalone::Result::Committed:
138
        case standalone::Result::Installed:
139
        case standalone::Result::RolledBack:
140
                // There should not be any error for these.
141
                assert(result.err == error::NoError);
142
                break;
143
        case standalone::Result::InstalledAndCommittedRebootRequired:
2✔
144
        case standalone::Result::InstalledRebootRequired:
145
                if (result.err == error::NoError) {
2✔
146
                        result.err = context::MakeError(context::RebootRequiredError, "Reboot required");
4✔
147
                }
148
                break;
149
        default:
20✔
150
                // All other states, make sure they have an error.
151
                if (result.err != error::NoError) {
20✔
152
                        log::Error(result.err.String());
28✔
153
                } else {
154
                        result.err = error::MakeError(error::ExitWithFailureError, "");
12✔
155
                }
156
                break;
157
        }
158

159
        switch (result.result) {
54✔
160
        case standalone::Result::InstalledAndCommitted:
19✔
161
        case standalone::Result::InstalledAndCommittedRebootRequired:
162
                cout << "Installed and committed." << endl;
19✔
163
                break;
19✔
164
        case standalone::Result::Committed:
3✔
165
                cout << "Committed." << endl;
3✔
166
                break;
3✔
167
        case standalone::Result::Installed:
9✔
168
        case standalone::Result::InstalledRebootRequired:
169
                cout << "Installed, but not committed." << endl;
9✔
170
                cout << "Use 'commit' to update, or 'rollback' to roll back the update." << endl;
9✔
171
                break;
9✔
172
        case standalone::Result::InstalledButFailedInPostCommit:
2✔
173
                cout << "Installed, but one or more post-commit steps failed." << endl;
2✔
174
                break;
2✔
175
        case standalone::Result::NoUpdateInProgress:
2✔
176
                cout << "No update in progress." << endl;
2✔
177
                break;
2✔
178
        case standalone::Result::FailedNothingDone:
5✔
179
                cout << "Installation failed. System not modified." << endl;
5✔
180
                break;
5✔
181
        case standalone::Result::RolledBack:
3✔
182
                cout << "Rolled back." << endl;
3✔
183
                break;
3✔
184
        case standalone::Result::NoRollback:
1✔
185
                cout << "Update Module does not support rollback." << endl;
1✔
186
                break;
1✔
187
        case standalone::Result::RollbackFailed:
1✔
188
                cout << "Rollback failed. System may be in an inconsistent state." << endl;
1✔
189
                break;
1✔
190
        case standalone::Result::FailedAndRolledBack:
1✔
191
                cout << "Installation failed. Rolled back modifications." << endl;
1✔
192
                break;
1✔
193
        case standalone::Result::FailedAndNoRollback:
4✔
194
                cout
195
                        << "Installation failed, and Update Module does not support rollback. System may be in an inconsistent state."
4✔
196
                        << endl;
4✔
197
                break;
4✔
198
        case standalone::Result::FailedAndRollbackFailed:
4✔
199
                cout
200
                        << "Installation failed, and rollback also failed. System may be in an inconsistent state."
4✔
201
                        << endl;
4✔
202
                break;
4✔
203
        }
204

205
        switch (result.result) {
54✔
206
        case standalone::Result::InstalledRebootRequired:
2✔
207
        case standalone::Result::InstalledAndCommittedRebootRequired:
208
                cout << "At least one payload requested a reboot of the device it updated." << endl;
2✔
209
                break;
2✔
210
        default:
211
                break;
212
        }
213

214
        return result.err;
54✔
215
}
216

217
error::Error InstallAction::Execute(context::MenderContext &main_context) {
44✔
218
        error::Error err = MaybeInstallBootstrapArtifact(main_context);
44✔
219
        if (err != error::NoError) {
44✔
220
                return err;
221
        }
222
        auto result = standalone::Install(main_context, src_);
44✔
223
        err = ResultHandler(result);
88✔
224
        if (!reboot_exit_code_
44✔
225
                && err.code == context::MakeError(context::RebootRequiredError, "").code) {
172✔
226
                // If reboot exit code isn't requested, then this type of error should be treated as
227
                // plain success.
228
                err = error::NoError;
1✔
229
        }
230
        return err;
231
}
232

233
error::Error CommitAction::Execute(context::MenderContext &main_context) {
4✔
234
        auto result = standalone::Commit(main_context);
4✔
235
        return ResultHandler(result);
8✔
236
}
237

238
error::Error RollbackAction::Execute(context::MenderContext &main_context) {
6✔
239
        auto result = standalone::Rollback(main_context);
6✔
240
        return ResultHandler(result);
12✔
241
}
242

243
error::Error DaemonAction::Execute(context::MenderContext &main_context) {
×
244
        events::EventLoop event_loop;
245
        daemon::Context ctx(main_context, event_loop);
×
246
        daemon::StateMachine state_machine(ctx, event_loop);
×
247
        state_machine.LoadStateFromDb();
×
248
        error::Error err = MaybeInstallBootstrapArtifact(main_context);
×
249
        if (err != error::NoError) {
×
250
                return err;
×
251
        }
UNCOV
252
        return state_machine.Run();
×
253
}
254

255
static expected::ExpectedString GetPID() {
×
UNCOV
256
        processes::Process proc({"systemctl", "show", "--property=MainPID", "mender-updated"});
×
257
        auto exp_line_data = proc.GenerateLineData();
×
UNCOV
258
        if (!exp_line_data) {
×
UNCOV
259
                return expected::unexpected(
×
260
                        exp_line_data.error().WithContext("Failed to get the MainPID from systemctl"));
×
261
        }
262
        if (exp_line_data.value().size() < 1) {
×
263
                return expected::unexpected(error::Error(
×
264
                        make_error_condition(errc::message_size), "No output received from systemctl"));
×
265
        }
UNCOV
266
        const string prefix {"MainPID="};
×
267
        const string line = exp_line_data.value().at(0);
×
268
        auto split_index = line.find(prefix);
×
269
        if (split_index == string::npos) {
×
UNCOV
270
                return expected::unexpected(error::Error(
×
271
                        make_error_condition(errc::no_message), "No output received from systemctl"));
×
272
        }
273
        if (split_index != 0) {
×
274
                return expected::unexpected(error::Error(
×
275
                        make_error_condition(errc::bad_message), "Unexpected output from systemctl"));
×
276
        }
UNCOV
277
        const string PID = line.substr(split_index + prefix.size(), line.size());
×
278
        if (PID == "" or PID == "0") {
×
279
                return expected::unexpected(error::Error(
×
280
                        make_error_condition(errc::no_message),
×
UNCOV
281
                        "No PID found for mender-updated. The service is not running"));
×
282
        }
283
        return PID;
×
284
}
285

286
static error::Error SendSignal(const string &signal, const string &pid) {
×
UNCOV
287
        const vector<string> command {"kill", "-" + signal, pid};
×
288
        const string command_string = common::JoinStrings(command, " ");
×
UNCOV
289
        processes::Process proc(command);
×
UNCOV
290
        auto err = proc.Start();
×
291
        if (err != error::NoError) {
×
292
                return err.WithContext("Command '" + command_string + "'");
×
293
        }
294
        return proc.Wait().WithContext("Command '" + command_string + "'");
×
295
}
296

297
error::Error SendInventoryAction::Execute(context::MenderContext &main_context) {
×
UNCOV
298
        auto pid = GetPID();
×
299
        if (!pid) {
×
UNCOV
300
                return pid.error().WithContext("Failed to force an inventory update");
×
301
        }
302

303
        return SendSignal("SIGUSR2", pid.value()).WithContext("Failed to force an inventory update");
×
304
}
305

UNCOV
306
error::Error CheckUpdateAction::Execute(context::MenderContext &main_context) {
×
UNCOV
307
        auto pid = GetPID();
×
308
        if (!pid) {
×
UNCOV
309
                return pid.error().WithContext("Failed to force an update check");
×
310
        }
311
        return SendSignal("SIGUSR1", pid.value()).WithContext("Failed to force an update check");
×
312
}
313

314
} // namespace cli
315
} // namespace update
316
} // 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