Implement support for the --env command-line argument

This commit is contained in:
Gunnar Beutner 2018-06-05 13:43:10 +02:00
parent c7e61748d4
commit c577554073
10 changed files with 179 additions and 27 deletions

View File

@ -32,6 +32,8 @@
#include "base/context.hpp"
#include "base/console.hpp"
#include "base/process.hpp"
#include "base/json.hpp"
#include "base/tcpsocket.hpp"
#include "config.h"
#include <boost/program_options.hpp>
#include <boost/algorithm/string/split.hpp>
@ -46,6 +48,9 @@
# include <Lmcons.h>
# include <Shellapi.h>
# include <tchar.h>
# define popen _popen
# define pclose _pclose
#endif /* _WIN32 */
using namespace icinga;
@ -215,8 +220,6 @@ static int Main()
Application::DeclareConcurrency(std::thread::hardware_concurrency());
Application::DeclareMaxConcurrentChecks(Application::GetDefaultMaxConcurrentChecks());
ScriptGlobal::Set("Environment", "production");
ScriptGlobal::Set("AttachDebugger", false);
ScriptGlobal::Set("PlatformKernel", Utility::GetPlatformKernel());
@ -247,7 +250,8 @@ static int Main()
("include,I", po::value<std::vector<std::string> >(), "add include search directory")
("log-level,x", po::value<std::string>(), "specify the log level for the console log.\n"
"The valid value is either debug, notice, information (default), warning, or critical")
("script-debugger,X", "whether to enable the script debugger");
("script-debugger,X", "whether to enable the script debugger")
("env", po::value<std::string>(), "the name of the environment, e.g. \"production\"");
po::options_description hiddenDesc("Hidden options");
@ -271,6 +275,99 @@ static int Main()
return EXIT_FAILURE;
}
if (vm.count("env") && vm["env"].as<std::string>() != "") {
String env = vm["env"].as<std::string>();
ScriptGlobal::Set("Environment", env);
} else {
#ifndef _WIN32
String cmd = "icinga-envs";
#else /* _WIN32 */
String cmd = "\"" + Utility::DirName(Application::GetExePath(argv[0])) + "\\icinga-envs.exe" + "\"";
#endif /* _WIN32 */
cmd += " get-default";
#ifndef _WIN32
cmd += " 2>/dev/null";
#else /* _WIN32 */
cmd += " 2>NUL";
cmd = "\"" + cmd + "\"";
#endif /* _WIN32 */
FILE *fp = popen(cmd.CStr(), "r");
std::stringstream msgbuf;
while (!ferror(fp) && !feof(fp)) {
char buf[512];
size_t num = fread(buf, 1, sizeof(buf), fp);
msgbuf << std::string(buf, buf + num);
}
pclose(fp);
String env = msgbuf.str();
ScriptGlobal::Set("Environment", env.Trim());
}
String env = ScriptGlobal::Get("Environment", NULL);
#ifndef _WIN32
String cmd = "icinga-envs";
#else /* _WIN32 */
String cmd = "\"" + Utility::DirName(Application::GetExePath(argv[0])) + "\\icinga-envs.exe" + "\"";
#endif /* _WIN32 */
cmd += " info --json " + Utility::EscapeShellArg(env);
#ifndef _WIN32
cmd += " 2>/dev/null";
#else /* _WIN32 */
cmd += " 2>NUL";
cmd = "\"" + cmd + "\"";
#endif /* _WIN32 */
FILE *fp = popen(cmd.CStr(), "r");
std::stringstream msgbuf;
while (!ferror(fp) && !feof(fp)) {
char buf[512];
size_t num = fread(buf, 1, sizeof(buf), fp);
msgbuf << std::string(buf, buf + num);
}
pclose(fp);
Dictionary::Ptr envInfo;
try {
envInfo = JsonDecode(msgbuf.str());
} catch (const std::exception&) {}
if (envInfo) {
Dictionary::Ptr config = envInfo->Get("config");
if (!config) {
Log(LogCritical, "app")
<< "Invalid JSON returned by icinga-envs: " << msgbuf.str();
return EXIT_FAILURE;
}
String configPath = config->Get("config_path");
String dataPath = config->Get("data_path");
ScriptGlobal::Set("SysconfDir", configPath);
ScriptGlobal::Set("RunDir", dataPath + "/run");
ScriptGlobal::Set("LocalStateDir", dataPath);
TcpSocket::Ptr agentListener = new TcpSocket();
agentListener->Bind("127.0.0.1", "0", AF_INET);
auto pieces = agentListener->GetClientAddress().Split(":");
config->Set("port", Convert::ToLong(pieces[pieces.size() - 1]));
Dictionary::Ptr envData = new Dictionary({
{ "listener", agentListener },
{ "config", config },
{ "meta_path", envInfo->Get("env_path") + "/config.json" }
});
// Make the agent listener available to the ApiListener later on
ScriptGlobal::Set("EnvironmentInfo", envData);
} else if (env != "") {
Log(LogCritical, "app")
<< "No such environment exists: " << env;
return EXIT_FAILURE;
}
#ifdef _WIN32
char username[UNLEN + 1];
DWORD usernameLen = UNLEN + 1;

View File

@ -10,9 +10,9 @@
<Binary Id="icinga2_installer" SourceFile="$<TARGET_FILE:icinga-installer>" />
<InstallExecuteSequence>
<Custom Action="XtraUpgradeNSIS" After="InstallInitialize">$CM_CP_sbin.icinga2_installer.exe&gt;2</Custom>
<Custom Action="XtraInstall" Before="InstallFinalize">$CM_CP_sbin.icinga2_installer.exe&gt;2</Custom>
<Custom Action="XtraUninstall" Before="RemoveExistingProducts">$CM_CP_sbin.icinga2_installer.exe=2</Custom>
<Custom Action="XtraUpgradeNSIS" After="InstallInitialize">$CM_CP_sbin.icinga2_installer.exe&gt;2 AND NOT SUPPRESS_XTRA</Custom>
<Custom Action="XtraInstall" Before="InstallFinalize">$CM_CP_sbin.icinga2_installer.exe&gt;2 AND NOT SUPPRESS_XTRA</Custom>
<Custom Action="XtraUninstall" Before="RemoveExistingProducts">$CM_CP_sbin.icinga2_installer.exe=2 AND NOT SUPPRESS_XTRA</Custom>
</InstallExecuteSequence>
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Run Icinga 2 setup wizard" />

View File

@ -49,6 +49,14 @@ int ApiSetupCommand::GetMaxArguments() const
return -1;
}
void ApiSetupCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
visibleDesc.add_options()
("quiet,q", "be less verbose")
;
}
/**
* The entry point for the "api setup" CLI command.
*
@ -56,12 +64,15 @@ int ApiSetupCommand::GetMaxArguments() const
*/
int ApiSetupCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
if (vm.count("quiet"))
Logger::SetConsoleLogSeverity(LogWarning);
String cn = VariableUtility::GetVariable("NodeName");
if (cn.IsEmpty())
cn = Utility::GetFQDN();
if (!ApiSetupUtility::SetupMaster(cn, true))
if (!ApiSetupUtility::SetupMaster(cn, true, vm.count("quiet")))
return 1;
return 0;

View File

@ -38,6 +38,8 @@ public:
String GetDescription() const override;
String GetShortDescription() const override;
int GetMaxArguments() const override;
void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
ImpersonationLevel GetImpersonationLevel() const override;
};

View File

@ -48,7 +48,7 @@ String ApiSetupUtility::GetApiUsersConfPath()
return ApiSetupUtility::GetConfdPath() + "/api-users.conf";
}
bool ApiSetupUtility::SetupMaster(const String& cn, bool prompt_restart)
bool ApiSetupUtility::SetupMaster(const String& cn, bool prompt_restart, bool quiet)
{
if (!SetupMasterCertificates(cn))
return false;
@ -56,13 +56,13 @@ bool ApiSetupUtility::SetupMaster(const String& cn, bool prompt_restart)
if (!SetupMasterApiUser())
return false;
if (!SetupMasterEnableApi())
if (!SetupMasterEnableApi(quiet))
return false;
if (!SetupMasterUpdateConstants(cn))
return false;
if (prompt_restart) {
if (prompt_restart && !quiet) {
std::cout << "Done.\n\n";
std::cout << "Now restart your Icinga 2 daemon to finish the installation!\n\n";
}
@ -196,11 +196,11 @@ bool ApiSetupUtility::SetupMasterApiUser()
return true;
}
bool ApiSetupUtility::SetupMasterEnableApi()
bool ApiSetupUtility::SetupMasterEnableApi(bool quiet)
{
Log(LogInformation, "cli", "Enabling the 'api' feature.");
FeatureUtility::EnableFeatures({ "api" });
FeatureUtility::EnableFeatures({ "api" }, quiet);
return true;
}

View File

@ -37,11 +37,11 @@ namespace icinga
class ApiSetupUtility
{
public:
static bool SetupMaster(const String& cn, bool prompt_restart = false);
static bool SetupMaster(const String& cn, bool prompt_restart = false, bool quiet = false);
static bool SetupMasterCertificates(const String& cn);
static bool SetupMasterApiUser();
static bool SetupMasterEnableApi();
static bool SetupMasterEnableApi(bool quiet = false);
static bool SetupMasterUpdateConstants(const String& cn);
static String GetConfdPath();

View File

@ -56,7 +56,7 @@ std::vector<String> FeatureUtility::GetFieldCompletionSuggestions(const String&
return suggestions;
}
int FeatureUtility::EnableFeatures(const std::vector<std::string>& features)
int FeatureUtility::EnableFeatures(const std::vector<std::string>& features, bool quiet)
{
String features_available_dir = GetFeaturesAvailablePath();
String features_enabled_dir = GetFeaturesEnabledPath();
@ -93,8 +93,9 @@ int FeatureUtility::EnableFeatures(const std::vector<std::string>& features)
continue;
}
std::cout << "Enabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
<< ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
if (!quiet)
std::cout << "Enabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
<< ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
#ifndef _WIN32
String relativeSource = "../features-available/" + feature + ".conf";
@ -131,7 +132,7 @@ int FeatureUtility::EnableFeatures(const std::vector<std::string>& features)
return 0;
}
int FeatureUtility::DisableFeatures(const std::vector<std::string>& features)
int FeatureUtility::DisableFeatures(const std::vector<std::string>& features, bool quiet)
{
String features_enabled_dir = GetFeaturesEnabledPath();
@ -160,8 +161,9 @@ int FeatureUtility::DisableFeatures(const std::vector<std::string>& features)
continue;
}
std::cout << "Disabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
<< ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
if (!quiet)
std::cout << "Disabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
<< ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
}
if (!errors.empty()) {

View File

@ -40,8 +40,8 @@ public:
static std::vector<String> GetFieldCompletionSuggestions(const String& word, bool enable);
static int EnableFeatures(const std::vector<std::string>& features);
static int DisableFeatures(const std::vector<std::string>& features);
static int EnableFeatures(const std::vector<std::string>& features, bool quiet = false);
static int DisableFeatures(const std::vector<std::string>& features, bool quiet = false);
static int ListFeatures(std::ostream& os = std::cout);
static bool GetFeatures(std::vector<String>& features, bool enable);

View File

@ -224,11 +224,23 @@ void ApiListener::Start(bool runtimeCreated)
OpenLogFile();
}
/* create the primary JSON-RPC listener */
if (!AddListener(GetBindHost(), GetBindPort())) {
Log(LogCritical, "ApiListener")
<< "Cannot add listener on host '" << GetBindHost() << "' for port '" << GetBindPort() << "'.";
Application::Exit(EXIT_FAILURE);
Dictionary::Ptr envData = ScriptGlobal::Get("EnvironmentInfo", &Empty);
if (envData) {
ScriptGlobal::Set("EnvironmentInfo", Empty);
TcpSocket::Ptr listener = envData->Get("listener");
if (!AddListener(listener)) {
Log(LogCritical, "ApiListener")
<< "Failed to set up pre-configured agent listener.";
Application::Exit(EXIT_FAILURE);
}
Utility::SaveJsonFile(envData->Get("meta_path"), 0600, envData->Get("config"));
} else {
/* create the primary JSON-RPC listener */
if (!AddListener(GetBindHost(), GetBindPort())) {
Log(LogCritical, "ApiListener")
<< "Cannot add listener on host '" << GetBindHost() << "' for port '" << GetBindPort() << "'.";
Application::Exit(EXIT_FAILURE);
}
}
m_Timer = new Timer();
@ -301,6 +313,33 @@ bool ApiListener::IsMaster() const
return master == GetLocalEndpoint();
}
/**
* Creates a new JSON-RPC listener using the specified TCP socket object.
*
* @param listener The TCP socket to use.
*/
bool ApiListener::AddListener(const TcpSocket::Ptr& listener)
{
ObjectLock olock(this);
std::shared_ptr<SSL_CTX> sslContext = m_SSLContext;
if (!sslContext) {
Log(LogCritical, "ApiListener", "SSL context is required for AddListener()");
return false;
}
Log(LogInformation, "ApiListener")
<< "Adding pre-configured listener on address " << listener->GetClientAddress();
std::thread thread(std::bind(&ApiListener::ListenerThreadProc, this, listener));
thread.detach();
m_Servers.insert(listener);
return true;
}
/**
* Creates a new JSON-RPC listener on the specified port.
*

View File

@ -138,6 +138,7 @@ private:
void CleanupCertificateRequestsTimerHandler();
bool AddListener(const String& node, const String& service);
bool AddListener(const TcpSocket::Ptr& listener);
void AddConnection(const Endpoint::Ptr& endpoint);
void NewClientHandler(const Socket::Ptr& client, const String& hostname, ConnectionRole role);