From 54ac042a19faf80f572a9fb5488aa5df7571a920 Mon Sep 17 00:00:00 2001
From: Lasse Tjernaes Wardenaer <lasse.tjernaes.wardenaer@cern.ch>
Date: Tue, 25 Oct 2022 08:33:26 +0200
Subject: [PATCH] Resolve "Configurable configuration folder for cta command
 line tools"

---
 ReleaseNotes.md         |  2 ++
 cmdline/CMakeLists.txt  |  2 +-
 cmdline/CtaAdminCmd.cpp | 26 +++++++++++++++++++++-----
 cmdline/CtaAdminCmd.hpp |  5 +++++
 4 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index 151e6ee771..8d609a97e0 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -1,4 +1,6 @@
 # v4.NEXT
+### Features 
+- cta/CTA#16 - Add option for a user config file
 
 ## Summary
 ### Features
diff --git a/cmdline/CMakeLists.txt b/cmdline/CMakeLists.txt
index 66d988f3b6..2aa5a2f841 100644
--- a/cmdline/CMakeLists.txt
+++ b/cmdline/CMakeLists.txt
@@ -33,7 +33,7 @@ include_directories(${CMAKE_BINARY_DIR}/eos_cta ${PROTOBUF3_INCLUDE_DIRS})
 # cta-admin <admin_command> is the SSI version of "cta <admin_command>"
 #
 add_executable(cta-admin CtaAdminCmd.cpp CtaAdminCmdParse.cpp CtaAdminTextFormatter.cpp)
-target_link_libraries(cta-admin XrdSsiPbEosCta XrdSsiLib XrdUtils ctacommon)
+target_link_libraries(cta-admin XrdSsiPbEosCta XrdSsiLib XrdUtils ctacommon stdc++fs)
 set_property (TARGET cta-admin APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
 
 #
diff --git a/cmdline/CtaAdminCmd.cpp b/cmdline/CtaAdminCmd.cpp
index fca4cdf396..0c01ca0c19 100644
--- a/cmdline/CtaAdminCmd.cpp
+++ b/cmdline/CtaAdminCmd.cpp
@@ -15,6 +15,7 @@
  *               submit itself to any jurisdiction.
  */
 
+#include <filesystem>
 #include <sstream>
 #include <iostream>
 
@@ -33,9 +34,10 @@ std::atomic<bool> isHeaderSent(false);
 // initialise an output buffer of 1000 lines
 cta::admin::TextFormatter formattedText(1000);
 
-
+const std::filesystem::path DEFAULT_CLI_CONFIG = "/etc/cta/cta-cli.conf";
 std::string tp_config_file = "/etc/cta/TPCONFIG";
 
+
 namespace XrdSsiPb {
 
 /*!
@@ -184,6 +186,8 @@ CtaAdminCmd::CtaAdminCmd(int argc, const char *const *const argv) :
 
    if(std::string(argv[argno]) == "--json") { is_json = true; ++argno; }
 
+   if(std::string(argv[argno]) == "--config") { m_config = argv[++argno]; ++argno; }
+
    // Commands, subcommands and server-side options
 
    if(argc <= argno || (cmd_it = cmdLookup.find(argv[argno++])) == cmdLookup.end()) {
@@ -226,7 +230,18 @@ CtaAdminCmd::CtaAdminCmd(int argc, const char *const *const argv) :
    parseOptions(has_subcommand ? argno+1 : argno, argc, argv, option_list_it->second);
 }
 
-
+const std::string CtaAdminCmd::getConfigFilePath() const {
+  std::filesystem::path config_file = DEFAULT_CLI_CONFIG;
+   
+  const std::filesystem::path home = std::getenv("HOME");
+  const std::filesystem::path home_dir_config_file = home / ".cta/cta-cli.conf";
+  if(std::filesystem::exists(home_dir_config_file)) { config_file = home_dir_config_file; }
+
+  if(m_config) {
+    config_file = m_config.value();
+  }
+  return config_file;
+}
 
 void CtaAdminCmd::send() const
 {
@@ -237,8 +252,9 @@ void CtaAdminCmd::send() const
       throwUsage(ex.what());
    }
 
+   const std::filesystem::path config_file = getConfigFilePath(); 
+
    // Set configuration options
-   const std::string config_file = "/etc/cta/cta-cli.conf";
    XrdSsiPb::Config config(config_file, "cta");
    config.set("resource", "/ctafrontend");
    config.set("response_bufsize", StreamBufferSize);         // default value = 1024 bytes
@@ -256,7 +272,7 @@ void CtaAdminCmd::send() const
 
    // Validate that endpoint was specified in the config file
    if(!config.getOptionValueStr("endpoint").first) {
-      throw std::runtime_error("Configuration error: cta.endpoint missing from " + config_file);
+      throw std::runtime_error("Configuration error: cta.endpoint missing from " + config_file.string());
    }
 
    // If the server is down, we want an immediate failure. Set client retry to a single attempt.
@@ -504,7 +520,7 @@ void CtaAdminCmd::throwUsage(const std::string &error_txt) const
    {
       // Command has not been set: show generic help
       help << "CTA Administration Tool" << std::endl << std::endl
-           << "Usage: " << m_execname << " [--json] <command> [<subcommand> [<option>...]]" << std::endl
+           << "Usage: " << m_execname << " [--json] [--config <configpath>] <command> [<subcommand> [<option>...]]" << std::endl
            << "       " << m_execname << " <command> help" << std::endl << std::endl
            << "By default, the output is in tabular format. If the --json option is supplied, the output is a JSON array." << std::endl
            << "Commands have a long and short version. Subcommands (add/ch/ls/rm/etc.) do not have short versions. For" << std::endl
diff --git a/cmdline/CtaAdminCmd.hpp b/cmdline/CtaAdminCmd.hpp
index f083a9b9fd..d741448862 100644
--- a/cmdline/CtaAdminCmd.hpp
+++ b/cmdline/CtaAdminCmd.hpp
@@ -61,6 +61,9 @@ private:
    //! Return the request timeout value (to pass to the ServiceClientSide constructor)
    int GetRequestTimeout() const;
 
+   //! Returns user config path if specified, if not looks in $HOME/.cta, then in /etc/cta
+   const std::string getConfigFilePath() const;
+
    // Member variables
 
    const std::string StreamBufferSize      = "1024";                  //!< Buffer size for Data/Stream Responses
@@ -77,6 +80,8 @@ private:
    static std::atomic<bool> is_json;                                  //!< Display results in JSON format
    static std::atomic<bool> is_first_record;                          //!< Delimiter for JSON records
 
+   std::optional<std::string> m_config;                                 //!< User defined config file
+
    static constexpr const char* const LOG_SUFFIX  = "CtaAdminCmd";    //!< Identifier for log messages
 };
 
-- 
GitLab