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

mendersoftware / mender / 1592735965

17 Dec 2024 03:49PM UTC coverage: 79.695% (-0.03%) from 79.728%
1592735965

Pull #1718

gitlab-ci

Mender
fix: Resend the inventory when the device has reauthenticated

When using mutual TLS authentication the devices are authenticated by the
gateway and will be automatically authorized in the server. This
reauthentication happens without posting any error codes to the callback
when polling for new deployments. Need to instead explcitly check if the
device has reauthenticated and clear the inventory cache if that's the case.

Ticket: MEN-7820
Changelog: Title

Signed-off-by: John Olav Lund <john.olav.lund@northern.tech>
(cherry picked from commit efc3fca21)
Pull Request #1718: [Cherry 4.0.x]: fix: Resend the inventory when the device has reauthenticated

2 of 5 new or added lines in 2 files covered. (40.0%)

1 existing line in 1 file now uncovered.

7214 of 9052 relevant lines covered (79.7%)

12087.1 hits per line

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

86.41
/src/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() {
106✔
37
        string download_command = "Download";
106✔
38
        if (download_->downloading_with_sizes_) {
106✔
39
                download_command = "DownloadWithFileSizes";
3✔
40
        }
41
        log::Debug(
106✔
42
                "Calling Update Module with command `" + update_module_path_ + " " + download_command + " "
212✔
43
                + update_module_workdir_ + "`.");
318✔
44
        download_->proc_ = make_shared<procs::Process>(
106✔
45
                vector<string> {update_module_path_, download_command, update_module_workdir_});
530✔
46

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

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

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

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

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

79
        DownloadErrorHandler(OpenStreamNextPipe(
212✔
80
                [this](io::ExpectedAsyncWriterPtr writer) { StreamNextOpenHandler(writer); }));
230✔
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) {
9✔
143
                        StreamNextWriteHandler(entry_size, result);
9✔
144
                }));
29✔
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) {
9✔
161
        // Close stream-next writer.
162
        download_->stream_next_writer_.reset();
9✔
163
        if (!result) {
9✔
UNCOV
164
                DownloadErrorHandler(result.error());
×
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
}
9✔
171

172
void UpdateModule::PayloadReadHandler(io::ExpectedSize result) {
611✔
173
        if (!result) {
611✔
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) {
611✔
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();
51✔
188
                download_->current_payload_reader_.reset();
51✔
189

190
                if (download_->downloading_to_files_) {
51✔
191
                        StartDownloadToFile();
46✔
192
                } else {
193
                        DownloadErrorHandler(OpenStreamNextPipe(
10✔
194
                                [this](io::ExpectedAsyncWriterPtr writer) { StreamNextOpenHandler(writer); }));
18✔
195
                }
196
        }
197
}
611✔
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,308✔
232
        if (err != error::NoError) {
1,308✔
233
                EndDownloadLoop(err);
10✔
234
        }
235
}
1,308✔
236

237
void UpdateModule::EndDownloadLoop(const error::Error &err) {
106✔
238
        download_->download_finished_handler_(err);
106✔
239
}
106✔
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) {
103✔
248
        if (err != error::NoError) {
103✔
249
                err = GetProcessError(err);
5✔
250
                DownloadErrorHandler(error::Error(
10✔
251
                        err.code, "Download: Update Module returned non-zero status: " + err.message));
10✔
252
        } else if (download_->module_has_finished_download_) {
98✔
253
                EndDownloadLoop(error::NoError);
3✔
254
        } else if (download_->module_has_started_download_) {
95✔
255
                DownloadErrorHandler(error::Error(
3✔
256
                        make_error_condition(errc::broken_pipe),
6✔
257
                        "Update Module started downloading, but did not finish"));
6✔
258
        } else {
259
                download_->downloading_to_files_ = true;
92✔
260
                download_->stream_next_opener_.reset();
92✔
261
                download_->current_stream_opener_.reset();
92✔
262
                err = DeleteStreamsFiles();
92✔
263
                if (err != error::NoError) {
92✔
264
                        DownloadErrorHandler(err);
×
265
                } else {
266
                        StartDownloadToFile();
92✔
267
                }
268
        }
269
}
103✔
270

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

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

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

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

308
        DownloadErrorHandler(download_->current_payload_reader_->AsyncRead(
92✔
309
                download_->buffer_.begin(), download_->buffer_.end(), [this](io::ExpectedSize result) {
46✔
310
                        PayloadReadHandler(result);
46✔
311
                }));
138✔
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