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

mendersoftware / mender / 1012473002

21 Sep 2023 02:17PM UTC coverage: 78.107% (-0.3%) from 78.44%
1012473002

push

gitlab-ci

lluiscampos
chore: log stdout output on errors when parsing UM yes/no commands

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

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

6468 of 8281 relevant lines covered (78.11%)

11106.28 hits per line

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

86.29
/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>(
202✔
45
                vector<string> {update_module_path_, download_command, update_module_workdir_});
606✔
46

47
        download_->proc_->SetWorkDir(update_module_workdir_);
101✔
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);
101✔
59
        if (err != error::NoError) {
101✔
60
                DownloadErrorHandler(GetProcessError(err));
×
61
                return;
×
62
        }
63

64
        err = download_->proc_->AsyncWait(
202✔
65
                download_->event_loop_,
101✔
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);
97✔
71
                        }
72
                },
99✔
73
                chrono::seconds(ctx_.GetConfig().module_timeout_seconds));
303✔
74
        if (err != error::NoError) {
101✔
75
                DownloadErrorHandler(err);
×
76
                return;
×
77
        }
78

79
        DownloadErrorHandler(OpenStreamNextPipe(
101✔
80
                [this](io::ExpectedAsyncWriterPtr writer) { StreamNextOpenHandler(writer); }));
211✔
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
6✔
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");
3✔
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_ =
10✔
111
                make_shared<events::io::AsyncReaderFromReader>(download_->event_loop_, progress_reader);
20✔
112
        download_->current_payload_name_ = payload_reader->Name();
10✔
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
        DownloadErrorHandler(PrepareAndOpenStreamPipe(
10✔
118
                stream_path, [this](io::ExpectedAsyncWriterPtr writer) { StreamOpenHandler(writer); }));
26✔
119

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

143
void UpdateModule::StreamOpenHandler(io::ExpectedAsyncWriterPtr writer) {
6✔
144
        if (!writer) {
6✔
145
                DownloadErrorHandler(writer.error());
×
146
                return;
×
147
        }
148
        download_->current_stream_writer_ = writer.value();
6✔
149

150
        DownloadErrorHandler(download_->current_payload_reader_->AsyncRead(
12✔
151
                download_->buffer_.begin(), download_->buffer_.end(), [this](io::ExpectedSize result) {
12✔
152
                        PayloadReadHandler(result);
6✔
153
                }));
30✔
154
}
155

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

168
void UpdateModule::PayloadReadHandler(io::ExpectedSize result) {
604✔
169
        if (!result) {
604✔
170
                // Close streams.
171
                download_->current_stream_writer_.reset();
×
172
                download_->current_payload_reader_.reset();
×
173
                DownloadErrorHandler(result.error());
×
174
        } else if (result.value() > 0) {
604✔
175
                DownloadErrorHandler(download_->current_stream_writer_->AsyncWrite(
1,114✔
176
                        download_->buffer_.begin(),
557✔
177
                        download_->buffer_.begin() + result.value(),
557✔
178
                        [this, result](io::ExpectedSize write_result) {
557✔
179
                                StreamWriteHandler(result.value(), write_result);
557✔
180
                        }));
2,785✔
181
        } else {
182
                // Close streams.
183
                download_->current_stream_writer_.reset();
47✔
184
                download_->current_payload_reader_.reset();
47✔
185

186
                if (download_->downloading_to_files_) {
47✔
187
                        StartDownloadToFile();
42✔
188
                } else {
189
                        DownloadErrorHandler(OpenStreamNextPipe(
5✔
190
                                [this](io::ExpectedAsyncWriterPtr writer) { StreamNextOpenHandler(writer); }));
14✔
191
                }
192
        }
193
}
604✔
194

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

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

227
void UpdateModule::DownloadErrorHandler(const error::Error &err) {
1,302✔
228
        if (err != error::NoError) {
1,302✔
229
                EndDownloadLoop(err);
9✔
230
        }
231
}
1,302✔
232

233
void UpdateModule::EndDownloadLoop(const error::Error &err) {
101✔
234
        download_->download_finished_handler_(err);
101✔
235
}
101✔
236

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

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

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

286
        auto stream_path = path::Join(update_module_workdir_, string("files"));
86✔
287
        auto err = PrepareDownloadDirectory(stream_path);
43✔
288
        if (err != error::NoError) {
43✔
289
                DownloadErrorHandler(err);
×
290
                return;
×
291
        }
292

293
        stream_path = path::Join(stream_path, download_->current_payload_name_);
43✔
294

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

304
        DownloadErrorHandler(download_->current_payload_reader_->AsyncRead(
84✔
305
                download_->buffer_.begin(), download_->buffer_.end(), [this](io::ExpectedSize result) {
84✔
306
                        PayloadReadHandler(result);
42✔
307
                }));
210✔
308
}
309

310
} // namespace v3
311
} // namespace update_module
312
} // namespace update
313
} // 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