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

mendersoftware / mender / 1504885822

21 Oct 2024 08:08AM UTC coverage: 76.257% (-0.05%) from 76.305%
1504885822

push

gitlab-ci

web-flow
Merge pull request #1676 from kacf/size_fixes

MEN-7613: Size fixes

48 of 72 new or added lines in 18 files covered. (66.67%)

3 existing lines in 2 files now uncovered.

7313 of 9590 relevant lines covered (76.26%)

11280.18 hits per line

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

76.92
/src/mender-update/inventory.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/inventory.hpp>
16

17
#include <functional>
18
#include <sstream>
19
#include <string>
20

21
#include <api/api.hpp>
22
#include <api/client.hpp>
23
#include <common/common.hpp>
24
#include <client_shared/conf.hpp>
25
#include <common/error.hpp>
26
#include <common/events.hpp>
27
#include <common/http.hpp>
28
#include <client_shared/inventory_parser.hpp>
29
#include <common/io.hpp>
30
#include <common/json.hpp>
31
#include <common/log.hpp>
32

33
namespace mender {
34
namespace update {
35
namespace inventory {
36

37
using namespace std;
38

39
namespace api = mender::api;
40
namespace common = mender::common;
41
namespace conf = mender::client_shared::conf;
42
namespace error = mender::common::error;
43
namespace events = mender::common::events;
44
namespace expected = mender::common::expected;
45
namespace http = mender::common::http;
46
namespace inv_parser = mender::client_shared::inventory_parser;
47
namespace io = mender::common::io;
48
namespace json = mender::common::json;
49
namespace log = mender::common::log;
50

51
const InventoryErrorCategoryClass InventoryErrorCategory;
52

53
const char *InventoryErrorCategoryClass::name() const noexcept {
×
54
        return "InventoryErrorCategory";
×
55
}
56

57
string InventoryErrorCategoryClass::message(int code) const {
×
58
        switch (code) {
×
59
        case NoError:
60
                return "Success";
×
61
        case BadResponseError:
62
                return "Bad response error";
×
63
        }
64
        assert(false);
65
        return "Unknown";
×
66
}
67

68
error::Error MakeError(InventoryErrorCode code, const string &msg) {
×
69
        return error::Error(error_condition(code, InventoryErrorCategory), msg);
1✔
70
}
71

72
const string uri = "/api/devices/v1/inventory/device/attributes";
73

74
error::Error PushInventoryData(
4✔
75
        const string &inventory_generators_dir,
76
        events::EventLoop &loop,
77
        api::Client &client,
78
        size_t &last_data_hash,
79
        APIResponseHandler api_handler) {
80
        auto ex_inv_data = inv_parser::GetInventoryData(inventory_generators_dir);
4✔
81
        if (!ex_inv_data) {
4✔
82
                return ex_inv_data.error();
×
83
        }
84
        auto &inv_data = ex_inv_data.value();
4✔
85

86
        if (inv_data.count("mender_client_version") != 0) {
8✔
87
                inv_data["mender_client_version"].push_back(conf::kMenderVersion);
3✔
88
        } else {
89
                inv_data["mender_client_version"] = {conf::kMenderVersion};
12✔
90
        }
91

92
        stringstream top_ss;
8✔
93
        top_ss << "[";
4✔
94
        auto key_vector = common::GetMapKeyVector(inv_data);
8✔
95
        std::sort(key_vector.begin(), key_vector.end());
4✔
96
        for (const auto &key : key_vector) {
17✔
97
                top_ss << R"({"name":")";
13✔
98
                top_ss << json::EscapeString(key);
13✔
99
                top_ss << R"(","value":)";
13✔
100
                if (inv_data[key].size() == 1) {
13✔
101
                        top_ss << "\"" + json::EscapeString(inv_data[key][0]) + "\"";
18✔
102
                } else {
103
                        stringstream items_ss;
8✔
104
                        items_ss << "[";
4✔
105
                        for (const auto &str : inv_data[key]) {
12✔
106
                                items_ss << "\"" + json::EscapeString(str) + "\",";
16✔
107
                        }
108
                        auto items_str = items_ss.str();
109
                        // replace the trailing comma with the closing square bracket
110
                        items_str[items_str.size() - 1] = ']';
4✔
111
                        top_ss << items_str;
4✔
112
                }
113
                top_ss << R"(},)";
13✔
114
        }
115
        auto payload = top_ss.str();
116
        if (payload[payload.size() - 1] == ',') {
4✔
117
                // replace the trailing comma with the closing square bracket
118
                payload.pop_back();
4✔
119
        }
120
        payload.push_back(']');
4✔
121

122
        size_t payload_hash = std::hash<string> {}(payload);
4✔
123
        if (payload_hash == last_data_hash) {
4✔
124
                log::Info("Inventory data unchanged, not submitting");
2✔
125
                loop.Post([api_handler]() { api_handler(error::NoError); });
8✔
126
                return error::NoError;
1✔
127
        }
128

129
        http::BodyGenerator payload_gen = [payload]() {
24✔
130
                return make_shared<io::StringReader>(payload);
3✔
131
        };
3✔
132

133
        auto req = make_shared<api::APIRequest>();
3✔
134
        req->SetPath(uri);
135
        req->SetMethod(http::Method::PUT);
3✔
136
        req->SetHeader("Content-Type", "application/json");
6✔
137
        req->SetHeader("Content-Length", to_string(payload.size()));
6✔
138
        req->SetHeader("Accept", "application/json");
6✔
139
        req->SetBodyGenerator(payload_gen);
3✔
140

141
        auto received_body = make_shared<vector<uint8_t>>();
3✔
142
        return client.AsyncCall(
143
                req,
144
                [received_body, api_handler](http::ExpectedIncomingResponsePtr exp_resp) {
3✔
145
                        if (!exp_resp) {
3✔
146
                                log::Error("Request to push inventory data failed: " + exp_resp.error().message);
×
147
                                api_handler(exp_resp.error());
×
148
                                return;
×
149
                        }
150

151
                        auto body_writer = make_shared<io::ByteWriter>(received_body);
3✔
152
                        auto resp = exp_resp.value();
3✔
153
                        auto content_length = resp->GetHeader("Content-Length");
6✔
154
                        auto ex_len = common::StringToLongLong(content_length.value());
3✔
155
                        if (!ex_len) {
3✔
156
                                log::Error("Failed to get content length from the inventory API response headers");
×
157
                                body_writer->SetUnlimited(true);
×
158
                        } else if (
159
                                ex_len.value() < 0
3✔
160
                                or static_cast<unsigned long long>(ex_len.value())
3✔
161
                                           > numeric_limits<size_t>::max()) {
162
                                // This is a ridiculuous limit, but we are mainly interested in
163
                                // catching corrupt data / mistakes here. Actually limiting memory
164
                                // usage in a useful way is something which should be thought
165
                                // through more carefully and maybe configurable.
NEW
166
                                api_handler(error::Error(
×
NEW
167
                                        make_error_condition(errc::result_out_of_range),
×
NEW
168
                                        "Content-Length out of range"));
×
169
                                return;
170
                        } else {
171
                                received_body->resize(static_cast<size_t>(ex_len.value()));
3✔
172
                        }
173
                        resp->SetBodyWriter(body_writer);
6✔
174
                },
175
                [received_body, api_handler, payload_hash, &last_data_hash](
3✔
176
                        http::ExpectedIncomingResponsePtr exp_resp) {
2✔
177
                        if (!exp_resp) {
3✔
178
                                log::Error("Request to push inventory data failed: " + exp_resp.error().message);
×
179
                                api_handler(exp_resp.error());
×
180
                                return;
×
181
                        }
182

183
                        auto resp = exp_resp.value();
3✔
184
                        auto status = resp->GetStatusCode();
3✔
185
                        if (status == http::StatusOK) {
3✔
186
                                log::Info("Inventory data submitted successfully");
4✔
187
                                last_data_hash = payload_hash;
2✔
188
                                api_handler(error::NoError);
4✔
189
                        } else {
190
                                auto ex_err_msg = api::ErrorMsgFromErrorResponse(*received_body);
1✔
191
                                string err_str;
192
                                if (ex_err_msg) {
1✔
193
                                        err_str = ex_err_msg.value();
1✔
194
                                } else {
195
                                        err_str = resp->GetStatusMessage();
×
196
                                }
197
                                api_handler(MakeError(
2✔
198
                                        BadResponseError,
199
                                        "Got unexpected response " + to_string(status)
2✔
200
                                                + " from inventory API: " + err_str));
2✔
201
                        }
202
                });
12✔
203
}
204

205
} // namespace inventory
206
} // namespace update
207
} // 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