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

mendersoftware / mender / 1033306216

11 Oct 2023 01:10PM UTC coverage: 80.166%. Remained the same
1033306216

push

gitlab-ci

lluiscampos
fix: Typo in command `send-inventory`

Ticket: None
Changelog: None

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

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

6479 of 8082 relevant lines covered (80.17%)

10736.79 hits per line

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

64.81
/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
        }
252
        return state_machine.Run();
×
253
}
254

255
static expected::ExpectedString GetPID() {
×
256
        processes::Process proc({"systemctl", "show", "--property=MainPID", "mender-updated"});
×
257
        auto exp_line_data = proc.GenerateLineData();
×
258
        if (!exp_line_data) {
×
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
        }
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) {
×
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
        }
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),
×
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) {
×
287
        const vector<string> command {"kill", "-" + signal, pid};
×
288
        const string command_string = common::JoinStrings(command, " ");
×
289
        processes::Process proc(command);
×
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) {
×
298
        auto pid = GetPID();
×
299
        if (!pid) {
×
300
                return pid.error().WithContext("Failed to force an inventory update");
×
301
        }
302

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

306
error::Error CheckUpdateAction::Execute(context::MenderContext &main_context) {
×
307
        auto pid = GetPID();
×
308
        if (!pid) {
×
309
                return pid.error().WithContext("Failed to force an update check");
×
310
        }
311
        return SendSignal("SIGUSR2", 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