Merge pull request #6490 from Icinga/revert-6414-feature/icinga-envs

Revert "Implement support for the --env command-line argument"
This commit is contained in:
Michael Friedrich 2018-07-26 17:19:04 +02:00 committed by GitHub
commit 41b9a0424e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 27 additions and 211 deletions

View File

@ -2793,35 +2793,3 @@ Add the global zone `global-templates` in case it did not exist.
global = true
}
EOF
## Using Multiple Environments <a id="distributed-monitoring-environments"></a>
In some cases it might be useful to run multiple Icinga instance on the same host. Two potential scenarios include:
* running different versions of the same monitoring configuration (e.g. production and testing)
* running disparate sets of checks for entirely unrelated monitoring environments (e.g. infrastructure and applications)
Icinga supports these scenarios by providing a global variable called `Environment`. It can be set like any other
global variable, e.g. in the `constants.conf` file or in a file in a global template zone.
The `Environment` variable can also be set using the `--env` command-line option. Setting the variable this way
has the added effect that Icinga tries to determine if the `Icinga Environments` add-on is available and
if so uses its CLI commands to determine the configuration and data paths. This command-line option also has the
effect of changing the listener port for cluster feature to a random port which is made available to the
`Icinga Environments` add-on through its `config.json` file.
When Icinga establishes a TLS connection to another cluster instance it automatically uses the SNI extension
to signal which endpoint it is attempting to connect to. On its own this can already be used to position multiple
Icinga instances behind a load balancer.
SNI example: `icinga2-client1.localdomain`
However, if the `Environment` variable is non-empty Icinga will append the environment name to the SNI hostname
like this:
SNI example with environment: `icinga2-client1.localdomain:production`
The Icinga Environments add-on provides a reverse proxy which does SNI-based port multiplexing. I.e., it uses a
single externally-visible TCP port (usually 5665) and forwards connections to one or more Icinga instances
which are bound to a local TCP port. It does so by inspecting the environment name that is sent as part of the
SNI extension.

View File

@ -32,8 +32,6 @@
#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>
@ -48,9 +46,6 @@
# include <Lmcons.h>
# include <Shellapi.h>
# include <tchar.h>
# define popen _popen
# define pclose _pclose
#endif /* _WIN32 */
using namespace icinga;
@ -220,6 +215,8 @@ 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());
@ -250,8 +247,7 @@ 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")
("env", po::value<std::string>(), "the name of the environment, e.g. \"production\"");
("script-debugger,X", "whether to enable the script debugger");
po::options_description hiddenDesc("Hidden options");
@ -275,99 +271,6 @@ 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 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>
<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>
</InstallExecuteSequence>
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Run Icinga 2 setup wizard" />

View File

@ -49,14 +49,6 @@ 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.
*
@ -64,15 +56,12 @@ void ApiSetupCommand::InitParameters(boost::program_options::options_description
*/
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, vm.count("quiet")))
if (!ApiSetupUtility::SetupMaster(cn, true))
return 1;
return 0;

View File

@ -38,8 +38,6 @@ 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 quiet)
bool ApiSetupUtility::SetupMaster(const String& cn, bool prompt_restart)
{
if (!SetupMasterCertificates(cn))
return false;
@ -56,13 +56,13 @@ bool ApiSetupUtility::SetupMaster(const String& cn, bool prompt_restart, bool qu
if (!SetupMasterApiUser())
return false;
if (!SetupMasterEnableApi(quiet))
if (!SetupMasterEnableApi())
return false;
if (!SetupMasterUpdateConstants(cn))
return false;
if (prompt_restart && !quiet) {
if (prompt_restart) {
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 quiet)
bool ApiSetupUtility::SetupMasterEnableApi()
{
Log(LogInformation, "cli", "Enabling the 'api' feature.");
FeatureUtility::EnableFeatures({ "api" }, quiet);
FeatureUtility::EnableFeatures({ "api" });
return true;
}

View File

@ -37,11 +37,11 @@ namespace icinga
class ApiSetupUtility
{
public:
static bool SetupMaster(const String& cn, bool prompt_restart = false, bool quiet = false);
static bool SetupMaster(const String& cn, bool prompt_restart = false);
static bool SetupMasterCertificates(const String& cn);
static bool SetupMasterApiUser();
static bool SetupMasterEnableApi(bool quiet = false);
static bool SetupMasterEnableApi();
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, bool quiet)
int FeatureUtility::EnableFeatures(const std::vector<std::string>& features)
{
String features_available_dir = GetFeaturesAvailablePath();
String features_enabled_dir = GetFeaturesEnabledPath();
@ -93,9 +93,8 @@ int FeatureUtility::EnableFeatures(const std::vector<std::string>& features, boo
continue;
}
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";
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";
@ -132,7 +131,7 @@ int FeatureUtility::EnableFeatures(const std::vector<std::string>& features, boo
return 0;
}
int FeatureUtility::DisableFeatures(const std::vector<std::string>& features, bool quiet)
int FeatureUtility::DisableFeatures(const std::vector<std::string>& features)
{
String features_enabled_dir = GetFeaturesEnabledPath();
@ -161,9 +160,8 @@ int FeatureUtility::DisableFeatures(const std::vector<std::string>& features, bo
continue;
}
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";
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, bool quiet = false);
static int DisableFeatures(const std::vector<std::string>& features, bool quiet = false);
static int EnableFeatures(const std::vector<std::string>& features);
static int DisableFeatures(const std::vector<std::string>& features);
static int ListFeatures(std::ostream& os = std::cout);
static bool GetFeatures(std::vector<String>& features, bool enable);

View File

@ -224,23 +224,11 @@ void ApiListener::Start(bool runtimeCreated)
OpenLogFile();
}
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);
}
/* 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();
@ -313,33 +301,6 @@ 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,7 +138,6 @@ 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);