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

mendersoftware / mender / 1054626264

30 Oct 2023 10:27AM UTC coverage: 80.137% (-0.06%) from 80.194%
1054626264

push

gitlab-ci

kacf
chore: Add many missing error checks and exception harnesses.

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

100 of 100 new or added lines in 7 files covered. (100.0%)

6887 of 8594 relevant lines covered (80.14%)

9361.04 hits per line

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

86.96
/mender-update/update_module/v3/update_module_download.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/update_module/v3/update_module.hpp>
16

17
#include <mender-update/progress_reader/progress_reader.hpp>
18

19
#include <common/events.hpp>
20
#include <common/events_io.hpp>
21
#include <common/log.hpp>
22
#include <common/path.hpp>
23
#include <common/processes.hpp>
24

25
namespace mender {
26
namespace update {
27
namespace update_module {
28
namespace v3 {
29

30
namespace log = mender::common::log;
31
namespace path = mender::common::path;
32
namespace processes = mender::common::processes;
33
namespace progress = mender::update::progress;
34

35

36
void UpdateModule::StartDownloadProcess() {
101✔
37
        string download_command = "Download";
101✔
38
        if (download_->downloading_with_sizes_) {
101✔
39
                download_command = "DownloadWithFileSizes";
3✔
40
        }
41
        log::Debug(
101✔
42
                "Calling Update Module with command `" + update_module_path_ + " " + download_command + " "
202✔
43
                + update_module_workdir_ + "`.");
303✔
44
        download_->proc_ = make_shared<procs::Process>(
101✔
45
                vector<string> {update_module_path_, download_command, update_module_workdir_});
505✔
46

47
        download_->proc_->SetWorkDir(update_module_workdir_);
48

49
        auto err = PrepareStreamNextPipe();
101✔
50
        if (err != error::NoError) {
101✔
51
                DownloadErrorHandler(err);
×
52
                return;
53
        }
54

55
        processes::OutputHandler stdout_handler {"Update Module output (stdout): "};
101✔
56
        processes::OutputHandler stderr_handler {"Update Module output (stderr): "};
101✔
57

58
        err = download_->proc_->Start(stdout_handler, stderr_handler);
303✔
59
        if (err != error::NoError) {
101✔
60
                DownloadErrorHandler(GetProcessError(err));
×
61
                return;
×
62
        }
63

64
        err = download_->proc_->AsyncWait(
101✔
65
                download_->event_loop_,
66
                [this](error::Error err) {
198✔
67
                        if (err.code == make_error_condition(errc::timed_out)) {
99✔
68
                                DownloadTimeoutHandler();
2✔
69
                        } else {
70
                                ProcessEndedHandler(err);
194✔
71
                        }
72
                },
99✔
73
                chrono::seconds(ctx_.GetConfig().module_timeout_seconds));
101✔
74
        if (err != error::NoError) {
101✔
75
                DownloadErrorHandler(err);
×
76
                return;
77
        }
78

79
        DownloadErrorHandler(OpenStreamNextPipe(
202✔
80
                [this](io::ExpectedAsyncWriterPtr writer) { StreamNextOpenHandler(writer); }));
220✔
81
}
82

83
void UpdateModule::StreamNextOpenHandler(io::ExpectedAsyncWriterPtr writer) {
13✔
84
        if (!writer) {
13✔
85
                DownloadErrorHandler(writer.error());
×
86
                return;
3✔
87
        }
88
        download_->stream_next_writer_ = writer.value();
13✔
89

90
        download_->module_has_started_download_ = true;
13✔
91

92
        auto reader = download_->payload_.Next();
13✔
93
        if (!reader) {
13✔
94
                if (reader.error().code
3✔
95
                        == artifact::parser_error::MakeError(
3✔
96
                                   artifact::parser_error::NoMorePayloadFilesError, "")
6✔
97
                                   .code) {
98
                        download_->module_has_finished_download_ = true;
3✔
99
                        log::Debug("Update Module finished all downloads");
6✔
100
                        EndStreamNext();
3✔
101
                } else {
102
                        DownloadErrorHandler(reader.error());
×
103
                }
104
                return;
3✔
105
        }
106
        auto payload_reader = make_shared<artifact::Reader>(std::move(reader.value()));
10✔
107

108
        auto progress_reader = make_shared<progress::Reader>(payload_reader, payload_reader->Size());
10✔
109

110
        download_->current_payload_reader_ =
111
                make_shared<events::io::AsyncReaderFromReader>(download_->event_loop_, progress_reader);
20✔
112
        download_->current_payload_name_ = payload_reader->Name();
20✔
113
        download_->current_payload_size_ = payload_reader->Size();
10✔
114

115
        auto stream_path =
116
                path::Join(update_module_workdir_, string("streams"), download_->current_payload_name_);
20✔
117
        auto err = PrepareAndOpenStreamPipe(
118
                stream_path, [this](io::ExpectedAsyncWriterPtr writer) { StreamOpenHandler(writer); });
22✔
119
        if (err != error::NoError) {
10✔
120
                DownloadErrorHandler(err);
×
121
                return;
122
        }
123

124
        string stream_next_string;
125
        if (download_->downloading_with_sizes_) {
10✔
126
                stream_next_string = path::Join("streams", download_->current_payload_name_) + " "
2✔
127
                                                         + to_string(download_->current_payload_size_);
4✔
128
        } else {
129
                stream_next_string = path::Join("streams", download_->current_payload_name_);
18✔
130
        }
131
        size_t entry_size = stream_next_string.size() + 1;
10✔
132
        if (entry_size > download_->buffer_.size()) {
10✔
133
                DownloadErrorHandler(error::Error(
×
134
                        make_error_condition(errc::no_buffer_space), "Payload name is too large for buffer"));
×
135
                return;
136
        }
137
        copy(stream_next_string.begin(), stream_next_string.end(), download_->buffer_.begin());
10✔
138
        download_->buffer_[entry_size - 1] = '\n';
10✔
139
        DownloadErrorHandler(download_->stream_next_writer_->AsyncWrite(
10✔
140
                download_->buffer_.begin(),
141
                download_->buffer_.begin() + entry_size,
142
                [this, entry_size](io::ExpectedSize result) {
10✔
143
                        StreamNextWriteHandler(entry_size, result);
10✔
144
                }));
30✔
145
}
146

147
void UpdateModule::StreamOpenHandler(io::ExpectedAsyncWriterPtr writer) {
6✔
148
        if (!writer) {
6✔
149
                DownloadErrorHandler(writer.error());
×
150
                return;
×
151
        }
152
        download_->current_stream_writer_ = writer.value();
6✔
153

154
        DownloadErrorHandler(download_->current_payload_reader_->AsyncRead(
12✔
155
                download_->buffer_.begin(), download_->buffer_.end(), [this](io::ExpectedSize result) {
6✔
156
                        PayloadReadHandler(result);
6✔
157
                }));
18✔
158
}
159

160
void UpdateModule::StreamNextWriteHandler(size_t expected_n, io::ExpectedSize result) {
10✔
161
        // Close stream-next writer.
162
        download_->stream_next_writer_.reset();
10✔
163
        if (!result) {
10✔
164
                DownloadErrorHandler(result.error());
1✔
165
        } else if (expected_n != result.value()) {
9✔
166
                DownloadErrorHandler(error::Error(
×
167
                        make_error_condition(errc::io_error),
×
168
                        "Unexpected number of written bytes to stream-next"));
×
169
        }
170
}
10✔
171

172
void UpdateModule::PayloadReadHandler(io::ExpectedSize result) {
607✔
173
        if (!result) {
607✔
174
                // Close streams.
175
                download_->current_stream_writer_.reset();
×
176
                download_->current_payload_reader_.reset();
×
177
                DownloadErrorHandler(result.error());
×
178
        } else if (result.value() > 0) {
607✔
179
                DownloadErrorHandler(download_->current_stream_writer_->AsyncWrite(
560✔
180
                        download_->buffer_.begin(),
181
                        download_->buffer_.begin() + result.value(),
560✔
182
                        [this, result](io::ExpectedSize write_result) {
1,680✔
183
                                StreamWriteHandler(result.value(), write_result);
560✔
184
                        }));
2,240✔
185
        } else {
186
                // Close streams.
187
                download_->current_stream_writer_.reset();
47✔
188
                download_->current_payload_reader_.reset();
47✔
189

190
                if (download_->downloading_to_files_) {
47✔
191
                        StartDownloadToFile();
42✔
192
                } else {
193
                        DownloadErrorHandler(OpenStreamNextPipe(
10✔
194
                                [this](io::ExpectedAsyncWriterPtr writer) { StreamNextOpenHandler(writer); }));
18✔
195
                }
196
        }
197
}
607✔
198

199
void UpdateModule::StreamWriteHandler(size_t expected_n, io::ExpectedSize result) {
560✔
200
        if (!result) {
560✔
201
                DownloadErrorHandler(result.error());
1✔
202
        } else if (expected_n != result.value()) {
559✔
203
                DownloadErrorHandler(error::Error(
×
204
                        make_error_condition(errc::io_error),
×
205
                        "Unexpected number of written bytes to download stream"));
×
206
        } else {
207
                download_->written_ += result.value();
559✔
208
                log::Trace("Wrote " + to_string(download_->written_) + " bytes to Update Module");
1,118✔
209
                DownloadErrorHandler(download_->current_payload_reader_->AsyncRead(
1,118✔
210
                        download_->buffer_.begin(), download_->buffer_.end(), [this](io::ExpectedSize result) {
559✔
211
                                PayloadReadHandler(result);
559✔
212
                        }));
1,677✔
213
        }
214
}
560✔
215

216
void UpdateModule::EndStreamNext() {
3✔
217
        // Empty write.
218
        DownloadErrorHandler(download_->stream_next_writer_->AsyncWrite(
3✔
219
                download_->buffer_.begin(), download_->buffer_.begin(), [this](io::ExpectedSize result) {
6✔
220
                        if (!result) {
3✔
221
                                DownloadErrorHandler(result.error());
×
222
                        } else {
223
                                DownloadErrorHandler(error::NoError);
3✔
224
                        }
225
                        // Close writer.
226
                        download_->stream_next_writer_.reset();
3✔
227
                        // No further action necessary. Now we just need to wait for the process to finish.
228
                }));
6✔
229
}
3✔
230

231
void UpdateModule::DownloadErrorHandler(const error::Error &err) {
1,298✔
232
        if (err != error::NoError) {
1,298✔
233
                EndDownloadLoop(err);
9✔
234
        }
235
}
1,298✔
236

237
void UpdateModule::EndDownloadLoop(const error::Error &err) {
101✔
238
        download_->download_finished_handler_(err);
101✔
239
}
101✔
240

241
void UpdateModule::DownloadTimeoutHandler() {
2✔
242
        download_->proc_->EnsureTerminated();
2✔
243
        EndDownloadLoop(error::Error(
2✔
244
                make_error_condition(errc::timed_out), "Update Module Download process timed out"));
4✔
245
}
2✔
246

247
void UpdateModule::ProcessEndedHandler(error::Error err) {
97✔
248
        if (err != error::NoError) {
97✔
249
                err = GetProcessError(err);
4✔
250
                DownloadErrorHandler(error::Error(
8✔
251
                        err.code, "Download: Update Module returned non-zero status: " + err.message));
8✔
252
        } else if (download_->module_has_finished_download_) {
93✔
253
                EndDownloadLoop(error::NoError);
3✔
254
        } else if (download_->module_has_started_download_) {
90✔
255
                DownloadErrorHandler(error::Error(
2✔
256
                        make_error_condition(errc::broken_pipe),
4✔
257
                        "Update Module started downloading, but did not finish"));
4✔
258
        } else {
259
                download_->downloading_to_files_ = true;
88✔
260
                download_->stream_next_opener_.reset();
88✔
261
                download_->current_stream_opener_.reset();
88✔
262
                err = DeleteStreamsFiles();
88✔
263
                if (err != error::NoError) {
88✔
264
                        DownloadErrorHandler(err);
×
265
                } else {
266
                        StartDownloadToFile();
88✔
267
                }
268
        }
269
}
97✔
270

271
void UpdateModule::StartDownloadToFile() {
130✔
272
        auto reader = download_->payload_.Next();
130✔
273
        if (!reader) {
130✔
274
                if (reader.error().code
87✔
275
                        == artifact::parser_error::MakeError(
87✔
276
                                   artifact::parser_error::NoMorePayloadFilesError, "")
174✔
277
                                   .code) {
278
                        log::Debug("Downloaded all files to `files` directory.");
174✔
279
                        EndDownloadLoop(error::NoError);
87✔
280
                } else {
281
                        DownloadErrorHandler(reader.error());
×
282
                }
283
                return;
87✔
284
        }
285
        auto payload_reader = make_shared<artifact::Reader>(std::move(reader.value()));
43✔
286
        download_->current_payload_reader_ =
287
                make_shared<events::io::AsyncReaderFromReader>(download_->event_loop_, payload_reader);
86✔
288
        download_->current_payload_name_ = payload_reader->Name();
43✔
289

290
        auto stream_path = path::Join(update_module_workdir_, string("files"));
86✔
291
        auto err = PrepareDownloadDirectory(stream_path);
43✔
292
        if (err != error::NoError) {
43✔
293
                DownloadErrorHandler(err);
×
294
                return;
295
        }
296

297
        stream_path = path::Join(stream_path, download_->current_payload_name_);
86✔
298

299
        auto current_stream_writer =
300
                make_shared<events::io::AsyncFileDescriptorWriter>(download_->event_loop_);
43✔
301
        err = current_stream_writer->Open(stream_path);
43✔
302
        if (err != error::NoError) {
43✔
303
                DownloadErrorHandler(err);
1✔
304
                return;
305
        }
306
        download_->current_stream_writer_ = current_stream_writer;
307

308
        DownloadErrorHandler(download_->current_payload_reader_->AsyncRead(
84✔
309
                download_->buffer_.begin(), download_->buffer_.end(), [this](io::ExpectedSize result) {
42✔
310
                        PayloadReadHandler(result);
42✔
311
                }));
126✔
312
}
313

314
} // namespace v3
315
} // namespace update_module
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