envchk.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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 "Status.hpp"
  24. #include "jutil.hpp"
  25. //
  26. // Inputs file format (json)
  27. //
  28. // {
  29. // "input-name" : [ <array of values> ],
  30. // ...
  31. // }
  32. bool processOptions(int argc, char *argv[]);
  33. bool validate();
  34. std::string getNextArg(int argc, char *argv[], int idx);
  35. bool dirExists(const std::string &dirName);
  36. bool fileExists(const std::string &fileName);
  37. std::vector<std::string> splitString(const std::string &input, const std::string &delim);
  38. void outputStatus(Status &status, enum statusMsg::msgLevel);
  39. // Default configuration directories
  40. EnvironmentType envType = XML;
  41. std::string masterSchemaFile = "environment.xsd";
  42. std::string configSchemaRelativeDir = ".." PATHSEPSTR "componentfiles" PATHSEPSTR "configschema" PATHSEPSTR "xsd" PATHSEPSTR;
  43. std::string configSchemaDir = "";
  44. std::string configSchemaPluginsDir = "";
  45. std::string envFile;
  46. enum statusMsg::msgLevel outputLevel = statusMsg::warning;
  47. bool verbose = false;
  48. bool validateHiddenNodes = false;
  49. EnvironmentMgr *pEnvMgr = nullptr;
  50. class CliException : public std::exception
  51. {
  52. public:
  53. explicit CliException(std::string reason) : m_reason(std::move(reason)) { }
  54. virtual const char *what() const noexcept override
  55. {
  56. return m_reason.c_str();
  57. }
  58. private:
  59. std::string m_reason;
  60. };
  61. void usage()
  62. {
  63. //
  64. // usage below documents options
  65. std::cout << std::endl;
  66. std::cout << "envchk <options> envfile" << std::endl;
  67. std::cout << std::endl;
  68. std::cout << " -d --schema-dir <path> : path to schema files. default (" << configSchemaDir << ")" << std::endl;
  69. std::cout << " -p --schema-plugins-dir <path> : path to plugin files, default (" << configSchemaPluginsDir << ")" << std::endl;
  70. std::cout << " -m --master-config <filename> : name of master schema file, default (" << masterSchemaFile << ")" << std::endl;
  71. std::cout << " -l --level <level> : output level (and above), e=error, w=warning, i=informational" << std::endl;
  72. std::cout << " -v --verbose : verbose output" << std::endl;
  73. std::cout << std::endl;
  74. }
  75. int main(int argc, char *argv[])
  76. {
  77. int rc = 0;
  78. //
  79. // Build the default directory for the schema files
  80. std::string processPath(queryCurrentProcessPath());
  81. configSchemaDir = processPath.substr(0, processPath.find_last_of(PATHSEPSTR)) + PATHSEPSTR + configSchemaRelativeDir;
  82. //
  83. // Print usage?
  84. if (argc == 1)
  85. {
  86. usage();
  87. return 0;
  88. }
  89. //
  90. // Read options and validate
  91. if (!processOptions(argc, argv))
  92. {
  93. return 1; // get out now
  94. }
  95. if (!validate())
  96. {
  97. usage();
  98. return 1;
  99. }
  100. //
  101. // Create an environment manager reference and load the schema
  102. try
  103. {
  104. pEnvMgr = getEnvironmentMgrInstance(envType);
  105. if (verbose)
  106. std::cout << "Loading schema defined by " << masterSchemaFile << std::endl;
  107. // note that these are hardcoded for HPCC at this time, but could be made into options
  108. std::map<std::string, std::string> cfgParms;
  109. cfgParms["buildset"] = "buildset.xml"; // Not used right now, and probably never will be
  110. std::string pluginsPath = configSchemaPluginsDir;
  111. if (!pEnvMgr->loadSchema(configSchemaDir, masterSchemaFile, cfgParms))
  112. {
  113. throw CliException(pEnvMgr->getLastSchemaMessage());
  114. }
  115. }
  116. catch (const ParseException &pe)
  117. {
  118. std::cerr << "There was a problem creating the environment manager instance: " << pe.what() << std::endl;
  119. }
  120. catch(const CliException &e)
  121. {
  122. std::cerr << "There was a problem loading the schema: " << e.what() << std::endl;
  123. }
  124. //
  125. // If there is an environment, load it and apply
  126. if (!envFile.empty())
  127. {
  128. if (pEnvMgr->loadEnvironment(envFile))
  129. {
  130. Status status;
  131. pEnvMgr->validate(status, validateHiddenNodes);
  132. outputStatus(status, outputLevel);
  133. }
  134. else
  135. {
  136. std::cerr << "There was a problem loading the environment: " << std::endl << pEnvMgr->getLastEnvironmentMessage() << std::endl;
  137. rc = 1;
  138. }
  139. }
  140. return rc;
  141. }
  142. void outputStatus(Status &status, enum statusMsg::msgLevel lvl)
  143. {
  144. for (int curLevel=lvl; curLevel <= statusMsg::fatal; ++curLevel)
  145. {
  146. std::vector<statusMsg> msgs = status.getMessages(static_cast<enum statusMsg::msgLevel>(curLevel), false);
  147. for (auto &msg: msgs)
  148. {
  149. std::string path;
  150. if (!msg.nodeId.empty())
  151. {
  152. auto pNode = pEnvMgr->findEnvironmentNodeById(msg.nodeId);
  153. if (pNode)
  154. {
  155. pNode->getPath(path);
  156. }
  157. }
  158. std::cerr << status.getStatusTypeString(msg.msgLevel) << " : Path=" << path;
  159. if (!msg.attribute.empty())
  160. std::cerr << "[" << msg.attribute << "]";
  161. std::cerr << " Message=" << msg.msg << std::endl;
  162. }
  163. }
  164. }
  165. bool processOptions(int argc, char *argv[])
  166. {
  167. bool rc = true;
  168. int idx = 1;
  169. std::string optName, optVal;
  170. bool checkDir = false;
  171. try
  172. {
  173. while (idx < argc)
  174. {
  175. optName = getNextArg(argc, argv, idx++);
  176. if (optName == "-d" || optName == "--schema-dir")
  177. {
  178. configSchemaDir = getNextArg(argc, argv, idx++) += PATHSEPSTR;
  179. }
  180. else if (optName == "-p" || optName == "--schema-plugins-dir")
  181. {
  182. configSchemaPluginsDir = getNextArg(argc, argv, idx++) += PATHSEPSTR;
  183. }
  184. else if (optName == "-m" || optName == "--master-config")
  185. {
  186. masterSchemaFile = getNextArg(argc, argv, idx++);
  187. }
  188. else if (optName == "-l" || optName == "--level")
  189. {
  190. std::string lvl = getNextArg(argc, argv, idx++);
  191. {
  192. if (lvl == "e")
  193. {
  194. outputLevel = statusMsg::error;
  195. }
  196. else if (lvl == "w")
  197. {
  198. outputLevel = statusMsg::warning;
  199. }
  200. else if (lvl == "i")
  201. {
  202. outputLevel = statusMsg::info;
  203. }
  204. else
  205. {
  206. throw CliException("Output level is not valid");
  207. }
  208. }
  209. }
  210. else if (optName == "-v" || optName == "--verbose")
  211. {
  212. verbose = true;
  213. }
  214. else if (idx == argc)
  215. {
  216. envFile = optName;
  217. }
  218. else
  219. {
  220. throw CliException("Parameters are incorrect");
  221. }
  222. }
  223. }
  224. catch(const CliException &e)
  225. {
  226. std::cerr << "There was an issue processing options: " << e.what() << std::endl;
  227. rc = false;
  228. }
  229. return rc;
  230. }
  231. bool validate()
  232. {
  233. if (!dirExists(configSchemaDir))
  234. {
  235. std::cerr << "Schema directory " << configSchemaDir << " does not exist" << std::endl;
  236. return false;
  237. }
  238. if (!configSchemaPluginsDir.empty() && !dirExists(configSchemaPluginsDir))
  239. {
  240. std::cerr << "Schema plugins directory " << configSchemaPluginsDir << " does not exist" << std::endl;
  241. return false;
  242. }
  243. if (!fileExists(configSchemaDir + masterSchemaFile))
  244. {
  245. std::cerr << "The master config file " << masterSchemaFile << " does not exist" << std::endl;
  246. return false;
  247. }
  248. if (!envFile.empty())
  249. {
  250. if (!fileExists(envFile))
  251. {
  252. std::cerr << "The environment file " << envFile << " does not exist" << std::endl;
  253. return false;
  254. }
  255. }
  256. return true;
  257. }
  258. std::string getNextArg(int argc, char *argv[], int idx)
  259. {
  260. if (idx < argc)
  261. {
  262. return std::string(argv[idx]);
  263. }
  264. throw CliException("Arguments exhausted when more expected");
  265. }
  266. bool dirExists(const std::string &dirName)
  267. {
  268. bool rc = true;
  269. struct stat info;
  270. if (stat(dirName.c_str(), &info) != 0)
  271. {
  272. rc = false;
  273. }
  274. rc = ((info.st_mode&S_IFMT)==S_IFDIR);
  275. return rc;
  276. }
  277. bool fileExists(const std::string &fileName)
  278. {
  279. struct stat info;
  280. return stat(fileName.c_str(), &info) == 0;
  281. }
  282. std::vector<std::string> splitString(const std::string &input, const std::string &delim)
  283. {
  284. size_t start = 0, end = 0, delimLen = delim.length();
  285. std::vector<std::string> list;
  286. while (end != std::string::npos)
  287. {
  288. end = input.find(delim, start);
  289. std::string item = input.substr(start, (end == std::string::npos) ? std::string::npos : end - start);
  290. if (!item.empty())
  291. list.push_back(item);
  292. if (end != std::string::npos)
  293. {
  294. start = end + delimLen;
  295. if (start >= input.length())
  296. end = std::string::npos;
  297. }
  298. }
  299. return list;
  300. }