1 //
2 // Copyright (C) 2022 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include "host/libs/config/instance_nums.h"
17 
18 #include <android-base/parseint.h>
19 #include <android-base/strings.h>
20 #include <gflags/gflags.h>
21 
22 #include "common/libs/utils/contains.h"
23 #include "common/libs/utils/flag_parser.h"
24 #include "host/libs/config/config_utils.h"
25 
26 namespace cuttlefish {
27 
28 // Failed result: The flag was specified in an invalid way
29 // Empty optional: The flag was not specified
30 // Present optional: The flag was specified with a valid value
ParseBaseInstanceFlag(std::vector<std::string> & flags)31 static Result<std::optional<std::int32_t>> ParseBaseInstanceFlag(
32     std::vector<std::string>& flags) {
33   int value = -1;
34   auto flag = GflagsCompatFlag("base_instance_num", value);
35   CF_EXPECT(flag.Parse(flags), "Flag parsing error");
36   return value > 0 ? value : std::optional<std::int32_t>();
37 }
38 
39 // Failed result: The flag was specified in an invalid way
40 // Empty optional: The flag was not specified
41 // Present optional: The flag was specified with a valid value
ParseNumInstancesFlag(std::vector<std::string> & flags)42 static Result<std::optional<std::int32_t>> ParseNumInstancesFlag(
43     std::vector<std::string>& flags) {
44   int value = -1;
45   auto flag = GflagsCompatFlag("num_instances", value);
46   CF_EXPECT(flag.Parse(flags), "Flag parsing error");
47   return value > 0 ? value : std::optional<std::int32_t>();
48 }
49 
50 // Failed result: The flag was specified in an invalid way
51 // Empty set: The flag was not specified
52 // Set with members: The flag was specified with a valid value
ParseInstanceNums(const std::string & instance_nums_str)53 static Result<std::vector<std::int32_t>> ParseInstanceNums(
54     const std::string& instance_nums_str) {
55   if (instance_nums_str == "") {
56     return {};
57   }
58   std::vector<std::int32_t> instance_nums;
59   std::vector<std::string> split_str =
60       android::base::Split(instance_nums_str, ",");
61   std::set<std::int32_t> duplication_check_set;
62   for (const auto& instance_num_str : split_str) {
63     std::int32_t instance_num;
64     CF_EXPECT(android::base::ParseInt(instance_num_str.c_str(), &instance_num),
65               "Unable to parse \"" << instance_num_str << "\" in "
66                                    << "`--instance_nums=\"" << instance_nums_str
67                                    << "\"`");
68     CF_EXPECT(!Contains(duplication_check_set, instance_num),
69               instance_num << " is duplicated in -instance_nums flag.");
70     duplication_check_set.insert(instance_num);
71     instance_nums.push_back(instance_num);
72   }
73   return instance_nums;
74 }
75 
76 // Failed result: The flag was specified in an invalid way
77 // Empty set: The flag was not specified
78 // Set with members: The flag was specified with a valid value
ParseInstanceNumsFlag(std::vector<std::string> & flags)79 static Result<std::vector<std::int32_t>> ParseInstanceNumsFlag(
80     std::vector<std::string>& flags) {
81   std::string value;
82   auto flag = GflagsCompatFlag("instance_nums", value);
83   CF_EXPECT(flag.Parse(flags), "Flag parsing error");
84   if (!value.empty()) {
85     return CF_EXPECT(ParseInstanceNums(value));
86   } else {
87     return {};
88   }
89 }
90 
FromFlags(const std::vector<std::string> & flags)91 InstanceNumsCalculator& InstanceNumsCalculator::FromFlags(
92     const std::vector<std::string>& flags) & {
93   std::vector<std::string> flags_copy = flags;
94   TrySet(base_instance_num_, ParseBaseInstanceFlag(flags_copy));
95   TrySet(num_instances_, ParseNumInstancesFlag(flags_copy));
96   TrySet(instance_nums_, ParseInstanceNumsFlag(flags_copy));
97   return *this;
98 }
99 
FromFlags(const std::vector<std::string> & flags)100 InstanceNumsCalculator InstanceNumsCalculator::FromFlags(
101     const std::vector<std::string>& flags) && {
102   return FromFlags(flags);
103 }
104 
105 // Failed result: The flag was specified in an invalid way
106 // Empty optional: The flag was not specified
107 // Present optional: The flag was specified with a valid value
GflagsBaseInstanceFlag()108 static Result<std::optional<std::int32_t>> GflagsBaseInstanceFlag() {
109   gflags::CommandLineFlagInfo info;
110   if (!gflags::GetCommandLineFlagInfo("base_instance_num", &info)) {
111     return {};
112   }
113   if (info.is_default) {
114     return {};
115   }
116   CF_EXPECT(info.type == "int32");
117   return std::atoi(info.current_value.c_str());
118 }
119 
120 // Failed result: The flag was specified in an invalid way
121 // Empty optional: The flag was not specified
122 // Present optional: The flag was specified with a valid value
GflagsNumInstancesFlag()123 static Result<std::optional<std::int32_t>> GflagsNumInstancesFlag() {
124   gflags::CommandLineFlagInfo info;
125   if (!gflags::GetCommandLineFlagInfo("num_instances", &info)) {
126     return {};
127   }
128   if (info.is_default) {
129     return {};
130   }
131   CF_EXPECT(info.type == "int32");
132   return std::atoi(info.current_value.c_str());
133 }
134 
135 // Failed result: The flag was specified in an invalid way
136 // Empty set: The flag was not specified
137 // Set with members: The flag was specified with a valid value
GflagsInstanceNumsFlag()138 static Result<std::vector<std::int32_t>> GflagsInstanceNumsFlag() {
139   gflags::CommandLineFlagInfo info;
140   if (!gflags::GetCommandLineFlagInfo("instance_nums", &info)) {
141     return {};
142   }
143   if (info.is_default) {
144     return {};
145   }
146   CF_EXPECT(info.type == "string");
147   auto contents = info.current_value;
148   return CF_EXPECT(ParseInstanceNums(contents));
149 }
150 
FromGlobalGflags()151 InstanceNumsCalculator& InstanceNumsCalculator::FromGlobalGflags() & {
152   TrySet(base_instance_num_, GflagsBaseInstanceFlag());
153   TrySet(num_instances_, GflagsNumInstancesFlag());
154   TrySet(instance_nums_, GflagsInstanceNumsFlag());
155   return *this;
156 }
157 
FromGlobalGflags()158 InstanceNumsCalculator InstanceNumsCalculator::FromGlobalGflags() && {
159   return FromGlobalGflags();
160 }
161 
BaseInstanceNum(std::int32_t num)162 InstanceNumsCalculator& InstanceNumsCalculator::BaseInstanceNum(
163     std::int32_t num) & {
164   base_instance_num_ = num;
165   return *this;
166 }
BaseInstanceNum(std::int32_t num)167 InstanceNumsCalculator InstanceNumsCalculator::BaseInstanceNum(
168     std::int32_t num) && {
169   return BaseInstanceNum(num);
170 }
171 
NumInstances(std::int32_t num)172 InstanceNumsCalculator& InstanceNumsCalculator::NumInstances(
173     std::int32_t num) & {
174   num_instances_ = num;
175   return *this;
176 }
NumInstances(std::int32_t num)177 InstanceNumsCalculator InstanceNumsCalculator::NumInstances(
178     std::int32_t num) && {
179   return NumInstances(num);
180 }
181 
InstanceNums(const std::string & nums)182 InstanceNumsCalculator& InstanceNumsCalculator::InstanceNums(
183     const std::string& nums) & {
184   TrySet(instance_nums_, ParseInstanceNums(nums));
185   return *this;
186 }
InstanceNums(const std::string & nums)187 InstanceNumsCalculator InstanceNumsCalculator::InstanceNums(
188     const std::string& nums) && {
189   return InstanceNums(nums);
190 }
191 
InstanceNums(std::vector<std::int32_t> set)192 InstanceNumsCalculator& InstanceNumsCalculator::InstanceNums(
193     std::vector<std::int32_t> set) & {
194   instance_nums_ = std::move(set);
195   return *this;
196 }
InstanceNums(std::vector<std::int32_t> set)197 InstanceNumsCalculator InstanceNumsCalculator::InstanceNums(
198     std::vector<std::int32_t> set) && {
199   return InstanceNums(std::move(set));
200 }
201 
202 template <typename T>
TrySet(T & field,Result<T> result)203 void InstanceNumsCalculator::TrySet(T& field, Result<T> result) {
204   if (result.ok()) {
205     field = std::move(*result);
206   } else {
207     // TODO(schuffelen): Combine both errors into one
208     setter_result_.error() = result.error();
209   }
210 }
211 
CalculateFromFlags()212 Result<std::vector<std::int32_t>> InstanceNumsCalculator::CalculateFromFlags() {
213   CF_EXPECT(Result<void>(setter_result_));
214   std::optional<std::vector<std::int32_t>> instance_nums_opt;
215   if (!instance_nums_.empty()) {
216     instance_nums_opt = instance_nums_;
217   }
218   // exactly one of these two should be given
219   CF_EXPECT(!instance_nums_opt || !base_instance_num_,
220             "At least one of --instance_nums or --base_instance_num"
221                 << "should be given to call CalculateFromFlags()");
222   CF_EXPECT(instance_nums_opt || base_instance_num_,
223             "InstanceNums and BaseInstanceNum are mutually exclusive");
224 
225   if (instance_nums_opt) {
226     if (num_instances_) {
227       CF_EXPECT(instance_nums_.size() == *num_instances_);
228     }
229     CF_EXPECT(instance_nums_.size() > 0, "no instance nums");
230     return instance_nums_;
231   }
232 
233   std::vector<std::int32_t> instance_nums;
234   for (int i = 0; i < num_instances_.value_or(1); i++) {
235     instance_nums.push_back(i + *base_instance_num_);
236   }
237   return instance_nums;
238 }
239 
Calculate()240 Result<std::vector<std::int32_t>> InstanceNumsCalculator::Calculate() {
241   CF_EXPECT(Result<void>(setter_result_));
242 
243   if (!instance_nums_.empty() || base_instance_num_) {
244     return CalculateFromFlags();
245   }
246 
247   std::vector<std::int32_t> instance_nums;
248   for (int i = 0; i < num_instances_.value_or(1); i++) {
249     instance_nums.push_back(i + GetInstance());
250   }
251   CF_EXPECT(instance_nums.size() > 0, "no instance nums");
252   return instance_nums;
253 }
254 
255 }  // namespace cuttlefish
256