envmod.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2017 HPCC Systems®.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include <stdio.h>
  14. #include <iostream>
  15. #include <string>
  16. #include <vector>
  17. #include "platform.h"
  18. #include <exception>
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include "EnvironmentMgr.hpp"
  22. #include "Exceptions.hpp"
  23. #include "mod_template_support/EnvModTemplate.hpp"
  24. #include "mod_template_support/TemplateExecutionException.hpp"
  25. #include "jutil.hpp"
  26. //
  27. // Inputs file format (json)
  28. //
  29. // {
  30. // "input-name" : [ <array of values> ],
  31. // ...
  32. // }
  33. bool processOptions(int argc, char *vargv[]);
  34. bool validate();
  35. std::string getNextArg(int argc, char *argv[], int idx);
  36. bool dirExists(const std::string &dirName);
  37. bool fileExists(const std::string &fileName);
  38. std::vector<std::string> splitString(const std::string &input, const std::string &delim);
  39. // Default configuration directories
  40. EnvironmentType envType = XML;
  41. std::string masterSchemaFile = "environment.xsd";
  42. std::string configSchemaDir = "";
  43. std::string modTemplateFile;
  44. std::string configSchemaPluginsDir = "";
  45. std::string envFile, envOutputFile;
  46. struct InputDef {
  47. std::string inputName;
  48. std::string inputValue;
  49. };
  50. std::vector<InputDef> userInputs;
  51. bool listInputs = false;
  52. EnvironmentMgr *pEnvMgr = nullptr;
  53. EnvModTemplate *pTemplate = nullptr;
  54. class CliException : public std::exception
  55. {
  56. public:
  57. explicit CliException(std::string reason) : m_reason(std::move(reason)) { }
  58. virtual const char *what() const noexcept override
  59. {
  60. return m_reason.c_str();
  61. }
  62. private:
  63. std::string m_reason;
  64. };
  65. void usage()
  66. {
  67. //
  68. // usage below documents options
  69. std::cout << std::endl;
  70. std::cout << "envmod <options>" << std::endl;
  71. std::cout << std::endl;
  72. std::cout << " Configuration Options:" << std::endl;
  73. std::cout << " -d --schema-dir <path> : path to schema files. default (" << configSchemaDir << ")" << std::endl;
  74. std::cout << " -p --schema-plugins-dir <path> : path to plugin files, default (" << configSchemaPluginsDir << ")" << std::endl;
  75. std::cout << " -m --master-config <filename> : name of master schema file, default (" << masterSchemaFile << ")" << std::endl;
  76. std::cout << std::endl;
  77. std::cout << " Execution Options:" << std::endl;
  78. std::cout << " -t --template <filepath> : filepath to modification template - required" << std::endl;
  79. std::cout << " -e --env <fullpath> : Optional filepath to environment file to modify" << std::endl;
  80. std::cout << " Omit to validate the modification template" << std::endl;
  81. std::cout << " --inputs : List the template inputs. If this option is specified, the template" << std::endl;
  82. std::cout << " inputs are listed and the command exits." << std::endl;
  83. std::cout << " --input-file <fullpath> : Optional full path to a file with defined inputs for the template" << std::endl;
  84. std::cout << " These would be combined with any command line inputs" << std::endl;
  85. std::cout << " -i --input <var=value> : Assign the indicated value to the variable input." << std::endl;
  86. std::cout << " The input must be defined in the template" << std::endl;
  87. std::cout << " value may be a comma separated list (no blanks) for multi-value inputs" << std::endl;
  88. std::cout << " Inputs given on the command line override any in an input file (--input-file)" << std::endl;
  89. std::cout << " -o --output [fullpath] : Optional. If present, results of applying template are saved." << std::endl;
  90. std::cout << " If fullpath is specified, results written to this file, " << std::endl;
  91. std::cout << " If not specified, input env file is overwritten" << std::endl;
  92. std::cout << " Omit the -o option to test the modification template" << std::endl;
  93. std::cout << std::endl;
  94. }
  95. int main(int argc, char *argv[])
  96. {
  97. InitModuleObjects();
  98. std::string modTemplateSchemaFile = std::string(hpccBuildInfo.componentDir) + PATHSEPSTR + "configschema" + PATHSEPSTR + "templates" + PATHSEPSTR + "schema" + PATHSEPSTR + "ModTemplateSchema.json";
  99. std::string processPath(queryCurrentProcessPath());
  100. configSchemaDir = std::string(hpccBuildInfo.componentDir) + PATHSEPSTR + "configschema" + PATHSEPSTR + "xsd" + PATHSEPSTR;
  101. if (argc == 1)
  102. {
  103. usage();
  104. return 0;
  105. }
  106. //
  107. // Read options and validate
  108. if (!processOptions(argc, argv))
  109. {
  110. return 1; // get out now
  111. }
  112. if (!validate())
  113. {
  114. usage();
  115. return 1;
  116. }
  117. //
  118. // Create an environment manager reference and load the schema
  119. try
  120. {
  121. pEnvMgr = getEnvironmentMgrInstance(envType);
  122. std::cout << "Loading schema defined by " << masterSchemaFile << "...";
  123. // note that these are hardcoded for HPCC at this time, but could be made into options
  124. std::map<std::string, std::string> cfgParms;
  125. cfgParms["buildset"] = "buildset.xml"; // Not used right now, and probably never will be
  126. cfgParms["support_libs"] = "libcfgsupport_addrequiredinstances";
  127. std::string pluginsPath = configSchemaPluginsDir;
  128. if (!pEnvMgr->loadSchema(configSchemaDir, masterSchemaFile, cfgParms))
  129. {
  130. throw CliException(pEnvMgr->getLastSchemaMessage());
  131. }
  132. }
  133. catch (const ParseException &pe)
  134. {
  135. std::cout << "There was a problem creating the environment manager instance: " << pe.what() << std::endl;
  136. }
  137. catch(const CliException &e)
  138. {
  139. std::cout << "There was a problem loading the schema: " << e.what() << std::endl;
  140. }
  141. //
  142. // Create the modification template
  143. try
  144. {
  145. pTemplate = new EnvModTemplate(pEnvMgr, modTemplateSchemaFile);
  146. pTemplate->loadTemplateFromFile(modTemplateFile);
  147. }
  148. catch (const TemplateException &te)
  149. {
  150. std::cout << "There was a problem loading the modification template: " << te.what() << std::endl;
  151. return 1;
  152. }
  153. //
  154. // If list inputs was given, list them and get out
  155. if (listInputs)
  156. {
  157. std::cout << "Template inputs:" << std::endl;
  158. auto templateInputs = pTemplate->getVariables();
  159. for (auto &pInput : templateInputs)
  160. {
  161. std::cout << pInput->getName() << " - " << pInput->getDescription() << std::endl;
  162. }
  163. return 0; // get out
  164. }
  165. //
  166. // Process the input file if given here
  167. //
  168. // If the user provided any inputs, assign them
  169. if (!userInputs.empty())
  170. {
  171. for (auto &userInput: userInputs)
  172. {
  173. try
  174. {
  175. auto pInput = pTemplate->getVariable(userInput.inputName);
  176. auto values = splitString(userInput.inputValue, ",");
  177. for (auto &value: values)
  178. {
  179. pInput->addValue(value);
  180. }
  181. }
  182. catch (const TemplateException &te)
  183. {
  184. std::cout << "There was a problem: " << te.what() << std::endl;
  185. return 0; // get out
  186. }
  187. }
  188. }
  189. //
  190. // If there is an environment, load it and apply
  191. if (!envFile.empty())
  192. {
  193. if (!pEnvMgr->loadEnvironment(envFile))
  194. {
  195. std::cout << "There was a problem loading the environment: " << std::endl << pEnvMgr->getLastEnvironmentMessage() << std::endl;
  196. return 1;
  197. }
  198. try
  199. {
  200. pTemplate->execute();
  201. }
  202. catch (const TemplateExecutionException &te)
  203. {
  204. std::cout << te.what() << std::endl;
  205. return 1;
  206. }
  207. //
  208. // Write results
  209. if (!envOutputFile.empty())
  210. {
  211. pEnvMgr->saveEnvironment(envOutputFile);
  212. std::cout << "Results written to " << envOutputFile << std::endl;
  213. }
  214. else
  215. {
  216. std::cout << "Resuls not saved." << std::endl;
  217. }
  218. }
  219. else
  220. {
  221. std::cout << "No problems found in the modification template" << std::endl;
  222. }
  223. std::cout << "Done" << std::endl;
  224. return 0;
  225. }
  226. bool processOptions(int argc, char *argv[])
  227. {
  228. bool rc = true;
  229. int idx = 1;
  230. std::string optName, optVal;
  231. bool checkDir = false;
  232. try
  233. {
  234. while (idx < argc)
  235. {
  236. optName = getNextArg(argc, argv, idx++);
  237. if (optName == "-d" || optName == "--schema-dir")
  238. {
  239. configSchemaDir = getNextArg(argc, argv, idx++) += PATHSEPSTR;
  240. }
  241. else if (optName == "-p" || optName == "--schema-plugins-dir")
  242. {
  243. configSchemaPluginsDir = getNextArg(argc, argv, idx++) += PATHSEPSTR;
  244. }
  245. else if (optName == "-m" || optName == "--master-config")
  246. {
  247. masterSchemaFile = getNextArg(argc, argv, idx++);
  248. }
  249. else if (optName == "-t" || optName == "--template")
  250. {
  251. modTemplateFile = getNextArg(argc, argv, idx++);
  252. }
  253. else if (optName == "-e" || optName == "--env")
  254. {
  255. envFile = getNextArg(argc, argv, idx++);
  256. }
  257. else if (optName == "-o" || optName == "--output")
  258. {
  259. // this is the default if no filename given
  260. envOutputFile = envFile;
  261. if (idx < argc)
  262. {
  263. envOutputFile = getNextArg(argc, argv, idx++);
  264. }
  265. }
  266. else if (optName == "-i" || optName == "--input")
  267. {
  268. std::string assign = getNextArg(argc, argv, idx++);
  269. std::size_t equalPos = assign.find_first_of('=');
  270. if (equalPos != std::string::npos)
  271. {
  272. InputDef input;
  273. input.inputName = assign.substr(0, equalPos);
  274. input.inputValue = assign.substr(equalPos+1);
  275. userInputs.emplace_back(input);
  276. }
  277. else
  278. {
  279. throw CliException("Invalid input variable assignement: " + assign);
  280. }
  281. }
  282. else if (optName == "--inputs")
  283. {
  284. listInputs = true;
  285. }
  286. }
  287. }
  288. catch(const CliException &e)
  289. {
  290. std::cout << "There was an issue processing option: " << e.what() << std::endl;
  291. rc = false;
  292. }
  293. return rc;
  294. }
  295. bool validate()
  296. {
  297. if (!dirExists(configSchemaDir))
  298. {
  299. std::cout << "Schema directory " << configSchemaDir << " does not exist" << std::endl;
  300. return false;
  301. }
  302. if (!dirExists(configSchemaPluginsDir))
  303. {
  304. std::cout << "Schema plugins directory " << configSchemaPluginsDir << " does not exist" << std::endl;
  305. return false;
  306. }
  307. if (!fileExists(configSchemaDir + masterSchemaFile))
  308. {
  309. std::cout << "The master config file " << masterSchemaFile << " does not exist" << std::endl;
  310. return false;
  311. }
  312. if (!fileExists(modTemplateFile))
  313. {
  314. std::cout << "The modification template " << modTemplateFile << " does not exist" << std::endl;
  315. return false;
  316. }
  317. if (!envFile.empty())
  318. {
  319. if (!fileExists(envFile))
  320. {
  321. std::cout << "The environment file " << envFile << " does not exist" << std::endl;
  322. return false;
  323. }
  324. }
  325. return true;
  326. }
  327. std::string getNextArg(int argc, char *argv[], int idx)
  328. {
  329. if (idx < argc)
  330. {
  331. return std::string(argv[idx]);
  332. }
  333. throw CliException("Arguments exhausted when more expected");
  334. }
  335. bool dirExists(const std::string &dirName)
  336. {
  337. bool rc = true;
  338. struct stat info;
  339. if (stat(dirName.c_str(), &info) != 0)
  340. {
  341. rc = false;
  342. }
  343. rc = ((info.st_mode&S_IFMT)==S_IFDIR);
  344. return rc;
  345. }
  346. bool fileExists(const std::string &fileName)
  347. {
  348. struct stat info;
  349. return stat(fileName.c_str(), &info) == 0;
  350. }
  351. std::vector<std::string> splitString(const std::string &input, const std::string &delim)
  352. {
  353. size_t start = 0, end = 0, delimLen = delim.length();
  354. std::vector<std::string> list;
  355. while (end != std::string::npos)
  356. {
  357. end = input.find(delim, start);
  358. std::string item = input.substr(start, (end == std::string::npos) ? std::string::npos : end - start);
  359. if (!item.empty())
  360. list.push_back(item);
  361. start = ((end > (std::string::npos - delimLen)) ? std::string::npos : end + delimLen);
  362. }
  363. return list;
  364. }