]> granicus.if.org Git - icinga2/commitdiff
Implement the --file and --syntax-only options for "icinga2 console"
authorGunnar Beutner <gunnar.beutner@netways.de>
Thu, 25 Aug 2016 17:14:54 +0000 (19:14 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Thu, 25 Aug 2016 17:14:54 +0000 (19:14 +0200)
fixes #12554
fixes #10675

doc/11-cli-commands.md
lib/cli/consolecommand.cpp
lib/cli/consolecommand.hpp

index 5a36d86e27296c40ad7e533eaf5087aa05612a40..31fea0ff467113cad6db36ac808999aef68152dc 100644 (file)
@@ -214,7 +214,9 @@ Once connected you can inspect variables and execute other expressions by enteri
     <3> =>
 
 
-You can use the `--eval` parameter to evaluate a single expression in batch mode. The output format for batch mode is JSON.
+You can use the `--eval` parameter to evaluate a single expression in batch mode. Using the `--file` option you can specify a file which should be evaluated. The output format for batch mode is JSON.
+
+The `--syntax-only` option can be used in combination with `--eval` or `--file` to check a script for syntax errors. In this mode the script is parsed to identify syntax errors but not evaluated.
 
 Here's an example that retrieves the command that was used by Icinga to check the `example.localdomain` host:
 
index ed7b37e2550c7d4946a1fd3eb089e3e1f2be2015..421e9fd2c38e4e8389b2323b5faa07a99ae9284e 100644 (file)
@@ -138,6 +138,8 @@ void ConsoleCommand::InitParameters(boost::program_options::options_description&
        visibleDesc.add_options()
                ("connect,c", po::value<std::string>(), "connect to an Icinga 2 instance")
                ("eval,e", po::value<std::string>(), "evaluate expression and terminate")
+               ("file,r", po::value<std::string>(), "evaluate a file and terminate")
+               ("syntax-only", "only validate syntax (requires --eval or --file)")
                ("sandbox", "enable sandbox mode")
        ;
 }
@@ -204,7 +206,7 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::stri
 
        scriptFrame.Self = scriptFrame.Locals;
 
-       if (!vm.count("eval"))
+       if (!vm.count("eval") && !vm.count("file"))
                std::cout << "Icinga 2 (version: " << Application::GetAppVersion() << ")\n";
 
        const char *addrEnv = getenv("ICINGA2_API_URL");
@@ -215,14 +217,36 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::stri
                addr = vm["connect"].as<std::string>();
 
        String command;
+       bool syntaxOnly = false;
+
+       if (vm.count("syntax-only")) {
+               if (vm.count("eval") || vm.count("file"))
+                       syntaxOnly = true;
+               else {
+                       std::cerr << "The option --syntax-only can only be used in combination with --eval or --file." << std::endl;
+                       return EXIT_FAILURE;
+               }
+       }
 
        if (vm.count("eval"))
                command = vm["eval"].as<std::string>();
+       else if (vm.count("file")) {
+               std::string fname = vm["file"].as<std::string>();
 
-       return RunScriptConsole(scriptFrame, addr, session, command);;
+               try {
+                       std::ifstream fp(fname.c_str());
+                       fp.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+                       command = String(std::istreambuf_iterator<char>(fp), std::istreambuf_iterator<char>());
+               } catch (const std::exception&) {
+                       std::cerr << "Could not read file '" << fname << "'." << std::endl;
+                       return EXIT_FAILURE;
+               }
+       }
+
+       return RunScriptConsole(scriptFrame, addr, session, command, syntaxOnly);
 }
 
-int ConsoleCommand::RunScriptConsole(ScriptFrame& scriptFrame, const String& addr, const String& session, const String& commandOnce)
+int ConsoleCommand::RunScriptConsole(ScriptFrame& scriptFrame, const String& addr, const String& session, const String& commandOnce, bool syntaxOnly)
 {
        std::map<String, String> lines;
        int next_line = 1;
@@ -341,7 +365,15 @@ incomplete:
 
                        if (!l_ApiClient) {
                                expr = ConfigCompiler::CompileText(fileName, command);
-                               result = Serialize(expr->Evaluate(scriptFrame), 0);
+
+                               /* This relies on the fact that - for syntax errors - CompileText()
+                                * returns an AST where the top-level expression is a 'throw'. */
+                               if (!syntaxOnly || dynamic_cast<ThrowExpression *>(expr)) {
+                                       if (syntaxOnly)
+                                               std::cerr << "    => " << command << std::endl;
+                                       result = Serialize(expr->Evaluate(scriptFrame), 0);
+                               } else
+                                       result = true;
                        } else {
                                boost::mutex mutex;
                                boost::condition_variable cv;
@@ -373,7 +405,7 @@ incomplete:
                                break;
                        }
                } catch (const ScriptError& ex) {
-                       if (ex.IsIncompleteExpression()) {
+                       if (ex.IsIncompleteExpression() && commandOnce.IsEmpty()) {
                                continuation = true;
                                goto incomplete;
                        }
index 978848e04490d8ead08ba1892babf57b7578bb47..331f226a47b33b30f451eeafed14a5cb7cd6c032 100644 (file)
@@ -47,7 +47,7 @@ public:
        virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
 
        static int RunScriptConsole(ScriptFrame& scriptFrame, const String& addr = String(),
-           const String& session = String(), const String& commandOnce = String());
+           const String& session = String(), const String& commandOnce = String(), bool syntaxOnly = false);
 
 private:
        mutable boost::mutex m_Mutex;