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

mendersoftware / mender / 969433301

16 Aug 2023 11:23AM UTC coverage: 79.041% (+0.01%) from 79.029%
969433301

push

gitlab-ci

oleorhagen
refac(conf): Store all configuration in MenderConfig

This moves all the default configuration variables into the `MenderConfig`
class, in which it is available under the `default_config` variable.

The config struct is then passed around the code, as opposed to having globally
accessible variables, like we had last time around.

Ticket: MEN-6673
Changelog: None

Signed-off-by: Ole Petter <ole.orhagen@northern.tech>

27 of 27 new or added lines in 9 files covered. (100.0%)

5325 of 6737 relevant lines covered (79.04%)

200.26 hits per line

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

67.69
/common/conf/conf.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 <common/conf.hpp>
16

17
#include <string>
18
#include <cstdlib>
19
#include <cerrno>
20

21
#include <common/error.hpp>
22
#include <common/expected.hpp>
23
#include <common/log.hpp>
24
#include <common/json.hpp>
25

26
namespace mender {
27
namespace common {
28
namespace conf {
29

30
using namespace std;
31
namespace error = mender::common::error;
32
namespace expected = mender::common::expected;
33
namespace log = mender::common::log;
34
namespace json = mender::common::json;
35

36
const ConfigErrorCategoryClass ConfigErrorCategory;
37

38
const char *ConfigErrorCategoryClass::name() const noexcept {
×
39
        return "ConfigErrorCategory";
×
40
}
41

42
string ConfigErrorCategoryClass::message(int code) const {
8✔
43
        switch (code) {
8✔
44
        case NoError:
×
45
                return "Success";
×
46
        case InvalidOptionsError:
8✔
47
                return "Invalid options given";
8✔
48
        default:
×
49
                return "Unknown";
×
50
        }
51
}
52

53
error::Error MakeError(ConfigErrorCode code, const string &msg) {
13✔
54
        return error::Error(error_condition(code, ConfigErrorCategory), msg);
26✔
55
}
56

57

58
string GetEnv(const string &var_name, const string &default_value) {
673✔
59
        const char *value = getenv(var_name.c_str());
673✔
60
        if (value == nullptr) {
673✔
61
                return string(default_value);
672✔
62
        } else {
63
                return string(value);
1✔
64
        }
65
}
66

67
ExpectedOptionValue CmdlineOptionsIterator::Next() {
293✔
68
        string option = "";
586✔
69
        string value = "";
586✔
70

71
        if (start_ + pos_ >= end_) {
293✔
72
                return ExpectedOptionValue({"", ""});
77✔
73
        }
74

75
        if (past_double_dash_) {
216✔
76
                OptionValue opt_val {"", start_[pos_]};
9✔
77
                pos_++;
3✔
78
                return ExpectedOptionValue(opt_val);
3✔
79
        }
80

81
        if (start_[pos_] == "--") {
213✔
82
                past_double_dash_ = true;
1✔
83
                pos_++;
1✔
84
                return ExpectedOptionValue({"--", ""});
1✔
85
        }
86

87
        if (start_[pos_][0] == '-') {
212✔
88
                auto eq_idx = start_[pos_].find('=');
93✔
89
                if (eq_idx != string::npos) {
93✔
90
                        option = start_[pos_].substr(0, eq_idx);
3✔
91
                        value = start_[pos_].substr(eq_idx + 1, start_[pos_].size() - eq_idx - 1);
3✔
92
                        pos_++;
3✔
93
                } else {
94
                        option = start_[pos_];
90✔
95
                        pos_++;
90✔
96
                }
97

98
                if (opts_with_value_.count(option) != 0) {
93✔
99
                        // option with value
100
                        if ((value == "") && ((start_ + pos_ >= end_) || (start_[pos_][0] == '-'))) {
84✔
101
                                // the next item is not a value
102
                                error::Error err = MakeError(
103
                                        ConfigErrorCode::InvalidOptionsError, "Option " + option + " missing value");
4✔
104
                                return ExpectedOptionValue(expected::unexpected(err));
4✔
105
                        } else if (value == "") {
82✔
106
                                // only assign the next item as value if there was no value
107
                                // specified as '--opt=value' (parsed above)
108
                                value = start_[pos_];
80✔
109
                                pos_++;
80✔
110
                        }
111
                } else if (opts_wo_value_.count(option) == 0) {
9✔
112
                        // unknown option
113
                        error::Error err = MakeError(
114
                                ConfigErrorCode::InvalidOptionsError, "Unrecognized option '" + option + "'");
8✔
115
                        return ExpectedOptionValue(expected::unexpected(err));
8✔
116
                } else if (value != "") {
5✔
117
                        // option without a value, yet, there was a value specified as '--opt=value' (parsed
118
                        // above)
119
                        error::Error err = MakeError(
120
                                ConfigErrorCode::InvalidOptionsError,
121
                                "Option " + option + " doesn't expect a value");
2✔
122
                        return ExpectedOptionValue(expected::unexpected(err));
2✔
123
                }
124
        } else {
125
                switch (mode_) {
119✔
126
                case ArgumentsMode::AcceptBareArguments:
37✔
127
                        value = start_[pos_];
37✔
128
                        pos_++;
37✔
129
                        break;
37✔
130
                case ArgumentsMode::RejectBareArguments:
3✔
131
                        return expected::unexpected(MakeError(
3✔
132
                                ConfigErrorCode::InvalidOptionsError,
133
                                "Unexpected argument '" + start_[pos_] + "'"));
6✔
134
                case ArgumentsMode::StopAtBareArguments:
79✔
135
                        return ExpectedOptionValue({"", ""});
79✔
136
                }
137
        }
138

139
        return ExpectedOptionValue({std::move(option), std::move(value)});
123✔
140
}
141

142
expected::ExpectedSize MenderConfig::ProcessCmdlineArgs(
78✔
143
        vector<string>::const_iterator start, vector<string>::const_iterator end) {
144
        bool explicit_config_path = false;
78✔
145
        bool explicit_fallback_config_path = false;
78✔
146
        string log_file = "";
156✔
147
        string log_level = log::ToStringLogLevel(log::kDefaultLogLevel);
156✔
148

149
        CmdlineOptionsIterator opts_iter(
150
                start,
151
                end,
152
                {"--config",
153
                 "-c",
154
                 "--fallback-config",
155
                 "-b",
156
                 "--data",
157
                 "-d",
158
                 "--log-file",
159
                 "-L",
160
                 "--log-level",
161
                 "-l"},
162
                {});
1,092✔
163
        opts_iter.SetArgumentsMode(ArgumentsMode::StopAtBareArguments);
78✔
164
        auto ex_opt_val = opts_iter.Next();
156✔
165
        while (ex_opt_val && ((ex_opt_val.value().option != "") || (ex_opt_val.value().value != ""))) {
153✔
166
                auto opt_val = ex_opt_val.value();
75✔
167
                if ((opt_val.option == "--config") || (opt_val.option == "-c")) {
75✔
168
                        paths.SetConfFile(opt_val.value);
×
169
                        explicit_config_path = true;
×
170
                } else if ((opt_val.option == "--fallback-config") || (opt_val.option == "-b")) {
75✔
171
                        paths.SetFallbackConfFile(opt_val.value);
×
172
                        explicit_fallback_config_path = true;
×
173
                } else if ((opt_val.option == "--data") || (opt_val.option == "-d")) {
75✔
174
                        paths.SetDataStore(opt_val.value);
75✔
175
                } else if ((opt_val.option == "--log-file") || (opt_val.option == "-L")) {
×
176
                        log_file = opt_val.value;
×
177
                } else if ((opt_val.option == "--log-level") || (opt_val.option == "-l")) {
×
178
                        log_level = opt_val.value;
×
179
                }
180
                ex_opt_val = opts_iter.Next();
75✔
181
        }
182
        if (!ex_opt_val) {
78✔
183
                return expected::unexpected(ex_opt_val.error());
×
184
        }
185

186
        if (log_file != "") {
78✔
187
                auto err = log::SetupFileLogging(log_file, true);
×
188
                if (error::NoError != err) {
×
189
                        return expected::unexpected(err);
×
190
                }
191
        }
192

193
        auto ex_log_level = log::StringToLogLevel(log_level);
156✔
194
        if (!ex_log_level) {
78✔
195
                return expected::unexpected(ex_log_level.error());
×
196
        }
197
        SetLevel(ex_log_level.value());
78✔
198

199
        auto err = LoadConfigFile_(paths.GetConfFile(), explicit_config_path);
156✔
200
        if (error::NoError != err) {
78✔
201
                this->Reset();
×
202
                return expected::unexpected(err);
×
203
        }
204

205
        err = LoadConfigFile_(paths.GetFallbackConfFile(), explicit_fallback_config_path);
78✔
206
        if (error::NoError != err) {
78✔
207
                this->Reset();
×
208
                return expected::unexpected(err);
×
209
        }
210

211
        return opts_iter.GetPos();
156✔
212
}
213

214
error::Error MenderConfig::LoadConfigFile_(const string &path, bool required) {
156✔
215
        auto ret = this->LoadFile(path);
312✔
216
        if (!ret) {
156✔
217
                if (required) {
156✔
218
                        // any failure when a file is required (e.g. path was given explicitly) means an error
219
                        log::Error("Failed to load config from '" + path + "': " + ret.error().message);
×
220
                        return ret.error();
×
221
                } else if (ret.error().IsErrno(ENOENT)) {
156✔
222
                        // File doesn't exist, OK for non-required
223
                        log::Debug("Failed to load config from '" + path + "': " + ret.error().message);
156✔
224
                        return error::NoError;
156✔
225
                } else {
226
                        // other errors (parsing errors,...) for default paths should produce warnings
227
                        log::Warning("Failed to load config from '" + path + "': " + ret.error().message);
×
228
                        return error::NoError;
×
229
                }
230
        }
231
        // else
232
        auto valid = this->ValidateConfig();
×
233
        if (!valid) {
×
234
                // validation error is always an error
235
                log::Error("Failed to validate config from '" + path + "': " + valid.error().message);
×
236
                return valid.error();
×
237
        }
238

239
        return error::NoError;
×
240
}
241

242
error::Error MenderConfig::LoadDefaults() {
×
243
        auto err = LoadConfigFile_(paths.GetFallbackConfFile(), false);
×
244
        if (error::NoError != err) {
×
245
                this->Reset();
×
246
                return err;
×
247
        }
248

249
        err = LoadConfigFile_(paths.GetConfFile(), false);
×
250
        if (error::NoError != err) {
×
251
                this->Reset();
×
252
                return err;
×
253
        }
254

255
        return error::NoError;
×
256
}
257

258
} // namespace conf
259
} // namespace common
260
} // 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