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

mendersoftware / mender / 1054591953

30 Oct 2023 10:02AM UTC coverage: 80.194% (+0.06%) from 80.137%
1054591953

push

gitlab-ci

kacf
fix: Make sure `device_type` is submitted with inventory data.

Changelog: None
Ticket: None

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

6871 of 8568 relevant lines covered (80.19%)

9388.93 hits per line

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

77.22
/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 <common/error.hpp>
25
#include <common/events.hpp>
26
#include <common/http.hpp>
27
#include <common/inventory_parser.hpp>
28
#include <common/io.hpp>
29
#include <common/json.hpp>
30
#include <common/log.hpp>
31
#include <common/path.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 error = mender::common::error;
42
namespace events = mender::common::events;
43
namespace expected = mender::common::expected;
44
namespace http = mender::http;
45
namespace inv_parser = mender::common::inventory_parser;
46
namespace io = mender::common::io;
47
namespace json = mender::common::json;
48
namespace log = mender::common::log;
49
namespace path = mender::common::path;
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(
3✔
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);
3✔
81
        if (!ex_inv_data) {
3✔
82
                return ex_inv_data.error();
×
83
        }
84

85
        stringstream top_ss;
6✔
86
        top_ss << "[";
3✔
87
        auto inv_data = ex_inv_data.value();
3✔
88
        auto key_vector = common::GetMapKeyVector(inv_data);
6✔
89
        std::sort(key_vector.begin(), key_vector.end());
3✔
90
        for (const auto &key : key_vector) {
12✔
91
                top_ss << R"({"name":")";
9✔
92
                top_ss << json::EscapeString(key);
9✔
93
                top_ss << R"(","value":)";
9✔
94
                if (inv_data[key].size() == 1) {
9✔
95
                        top_ss << "\"" + json::EscapeString(inv_data[key][0]) + "\"";
12✔
96
                } else {
97
                        stringstream items_ss;
6✔
98
                        items_ss << "[";
3✔
99
                        for (const auto &str : inv_data[key]) {
9✔
100
                                items_ss << "\"" + json::EscapeString(str) + "\",";
12✔
101
                        }
102
                        auto items_str = items_ss.str();
103
                        // replace the trailing comma with the closing square bracket
104
                        items_str[items_str.size() - 1] = ']';
3✔
105
                        top_ss << items_str;
3✔
106
                }
107
                top_ss << R"(},)";
9✔
108
        }
109
        auto payload = top_ss.str();
110
        // replace the trailing comma with the closing square bracket
111
        payload[payload.size() - 1] = ']';
3✔
112

113
        size_t payload_hash = std::hash<string> {}(payload);
3✔
114
        if (payload_hash == last_data_hash) {
3✔
115
                loop.Post([api_handler]() { api_handler(error::NoError); });
8✔
116
                return error::NoError;
1✔
117
        }
118

119
        http::BodyGenerator payload_gen = [payload]() {
16✔
120
                return make_shared<io::StringReader>(payload);
2✔
121
        };
2✔
122

123
        auto req = make_shared<api::APIRequest>();
2✔
124
        req->SetPath(uri);
125
        req->SetMethod(http::Method::PUT);
2✔
126
        req->SetHeader("Content-Type", "application/json");
4✔
127
        req->SetHeader("Content-Length", to_string(payload.size()));
4✔
128
        req->SetHeader("Accept", "application/json");
4✔
129
        req->SetBodyGenerator(payload_gen);
2✔
130

131
        auto received_body = make_shared<vector<uint8_t>>();
2✔
132
        return client.AsyncCall(
133
                req,
134
                [received_body, api_handler](http::ExpectedIncomingResponsePtr exp_resp) {
2✔
135
                        if (!exp_resp) {
2✔
136
                                log::Error("Request to push inventory data failed: " + exp_resp.error().message);
×
137
                                api_handler(exp_resp.error());
×
138
                                return;
×
139
                        }
140

141
                        auto body_writer = make_shared<io::ByteWriter>(received_body);
2✔
142
                        auto resp = exp_resp.value();
2✔
143
                        auto content_length = resp->GetHeader("Content-Length");
4✔
144
                        auto ex_len = common::StringToLongLong(content_length.value());
2✔
145
                        if (!ex_len) {
2✔
146
                                log::Error("Failed to get content length from the inventory API response headers");
×
147
                                body_writer->SetUnlimited(true);
×
148
                        } else {
149
                                received_body->resize(ex_len.value());
2✔
150
                        }
151
                        resp->SetBodyWriter(body_writer);
4✔
152
                },
153
                [received_body, api_handler, payload_hash, &last_data_hash](
2✔
154
                        http::ExpectedIncomingResponsePtr exp_resp) {
1✔
155
                        if (!exp_resp) {
2✔
156
                                log::Error("Request to push inventory data failed: " + exp_resp.error().message);
×
157
                                api_handler(exp_resp.error());
×
158
                                return;
×
159
                        }
160

161
                        auto resp = exp_resp.value();
2✔
162
                        auto status = resp->GetStatusCode();
2✔
163
                        if (status == http::StatusOK) {
2✔
164
                                last_data_hash = payload_hash;
1✔
165
                                api_handler(error::NoError);
2✔
166
                        } else {
167
                                auto ex_err_msg = api::ErrorMsgFromErrorResponse(*received_body);
1✔
168
                                string err_str;
169
                                if (ex_err_msg) {
1✔
170
                                        err_str = ex_err_msg.value();
1✔
171
                                } else {
172
                                        err_str = resp->GetStatusMessage();
×
173
                                }
174
                                api_handler(MakeError(
2✔
175
                                        BadResponseError,
176
                                        "Got unexpected response " + to_string(status)
2✔
177
                                                + " from inventory API: " + err_str));
2✔
178
                        }
179
                });
8✔
180
}
181

182
} // namespace inventory
183
} // namespace update
184
} // 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