[icinga-checkins] icinga.org: icinga2/master: Implement the --file and --syntax-only options for "icinga2 console"

git at icinga.org git at icinga.org
Thu Aug 25 19:15:34 CEST 2016


Module: icinga2
Branch: master
Commit: 1236495dd3c1bbfb2a9f413a5c9cf497e4fca26e
URL:    https://git.icinga.org/?p=icinga2.git;a=commit;h=1236495dd3c1bbfb2a9f413a5c9cf497e4fca26e

Author: Gunnar Beutner <gunnar.beutner at netways.de>
Date:   Thu Aug 25 19:14:54 2016 +0200

Implement the --file and --syntax-only options for "icinga2 console"

fixes #12554
fixes #10675

---

 doc/11-cli-commands.md     |    4 +++-
 lib/cli/consolecommand.cpp |   42 +++++++++++++++++++++++++++++++++++++-----
 lib/cli/consolecommand.hpp |    2 +-
 3 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/doc/11-cli-commands.md b/doc/11-cli-commands.md
index 5a36d86..31fea0f 100644
--- a/doc/11-cli-commands.md
+++ b/doc/11-cli-commands.md
@@ -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:
 
diff --git a/lib/cli/consolecommand.cpp b/lib/cli/consolecommand.cpp
index ed7b37e..421e9fd 100644
--- a/lib/cli/consolecommand.cpp
+++ b/lib/cli/consolecommand.cpp
@@ -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;
 			}
diff --git a/lib/cli/consolecommand.hpp b/lib/cli/consolecommand.hpp
index 978848e..331f226 100644
--- a/lib/cli/consolecommand.hpp
+++ b/lib/cli/consolecommand.hpp
@@ -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;



More information about the icinga-checkins mailing list