Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HPCC-30390 Improve get dafilesrv service errors #17837

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion dali/base/dautils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "platform.h"
#include "jlib.hpp"
#include "jcontainerized.hpp"
#include "jstring.hpp"
#include "jfile.hpp"
#include "jmisc.hpp"
Expand Down Expand Up @@ -3628,7 +3629,7 @@ void remapGroupsToDafilesrv(IPropertyTree *file, INamedGroupStore *resolver)
CriticalBlock b(dafileSrvNodeCS);
if (nullptr == dafileSrvNode)
{
auto externalService = getDafileServiceFromConfig("directio");
auto externalService = k8s::getDafileServiceFromConfig("directio");
VStringBuffer dafilesrvEpStr("%s:%u", externalService.first.c_str(), externalService.second);
dafileSrvNode.setown(createINode(dafilesrvEpStr));
}
Expand Down
3 changes: 2 additions & 1 deletion esp/clients/ws_dfsclient/ws_dfsclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <vector>

#include "jliball.hpp"
#include "jcontainerized.hpp"
#include "jflz.hpp"
#include "jsecrets.hpp"
#include "seclib.hpp"
Expand Down Expand Up @@ -726,7 +727,7 @@ IDFSFile *lookupDFSFile(const char *logicalName, AccessMode accessMode, unsigned
const IPropertyTree &eclWatch = eclWatchServices->query();
StringBuffer eclWatchName;
eclWatch.getProp("@name", eclWatchName);
auto result = getExternalService(eclWatchName);
auto result = k8s::getExternalService(eclWatchName);
if (result.first.empty())
throw makeStringExceptionV(-1, "dfs '%s': service not found", eclWatchName.str());
if (0 == result.second)
Expand Down
5 changes: 3 additions & 2 deletions esp/services/ws_dfu/ws_dfuService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include <math.h>

#include "jcontainerized.hpp"
#include "daclient.hpp"
#include "daft.hpp"
#include "daftcfg.hpp"
Expand Down Expand Up @@ -6116,7 +6117,7 @@ void CWsDfuEx::dFUFileAccessCommon(IEspContext &context, const CDfsLogicalFileNa
if (!info)
throw makeStringExceptionV(-1, "dFUFileAccessCommon: file signing certificate ('%s') not defined in configuration.", keyPairName.str());

auto externalService = getDafileServiceFromConfig("stream");
auto externalService = k8s::getDafileServiceFromConfig("stream");
dafilesrvHost.set(externalService.first.c_str());
port = externalService.second;
secure = true;
Expand Down Expand Up @@ -6498,7 +6499,7 @@ bool CWsDfuEx::onDFUFileCreateV2(IEspContext &context, IEspDFUFileCreateV2Reques
fileDesc.setown(createFileDescriptor(tempFileName, planeName, numParts));
numParts = fileDesc->numParts();

auto externalService = getDafileServiceFromConfig("stream");
auto externalService = k8s::getDafileServiceFromConfig("stream");
dafilesrvHost.set(externalService.first.c_str());
port = externalService.second;
secure = true;
Expand Down
96 changes: 96 additions & 0 deletions system/jlib/jcontainerized.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
limitations under the License.
############################################################################## */

#include "jerror.hpp"
#include "jexcept.hpp"
#include "jfile.hpp"
#include "jmisc.hpp"
Expand Down Expand Up @@ -292,6 +293,101 @@ std::vector<std::vector<std::string>> getPodNodes(const char *selector)
}
}

void runKubectlCommand(const char *title, const char *cmd, const char *input, StringBuffer *output)
{
#ifndef _CONTAINERIZED
UNIMPLEMENTED_X("runKubectlCommand");
#endif
// NB: will fire an exception if command fails (returns non-zero exit code)

StringBuffer _output, error;
if (!output)
output = &_output;
unsigned ret = runExternalCommand(title, *output, error, cmd, input, ".", nullptr);
if (output->length())
MLOG(MCdebugInfo, unknownJob, "%s: ret=%u, stdout=%s", cmd, ret, output->trimRight().str());
if (error.length())
MLOG(MCdebugError, unknownJob, "%s: ret=%u, stderr=%s", cmd, ret, error.trimRight().str());
if (ret)
{
if (input)
MLOG(MCdebugError, unknownJob, "Using input %s", input);
throw makeStringExceptionV(0, "Failed to run %s: error %u: %s", cmd, ret, error.str());
}
}

static CTimeLimitedCache<std::string, std::pair<std::string, unsigned>> externalServiceCache;
static CriticalSection externalServiceCacheCrit;
std::pair<std::string, unsigned> getExternalService(const char *serviceName)
{
#ifndef _CONTAINERIZED
UNIMPLEMENTED_X("getExternalService");
#endif
{
CriticalBlock b(externalServiceCacheCrit);
std::pair<std::string, unsigned> cachedExternalSevice;
if (externalServiceCache.get(serviceName, cachedExternalSevice))
return cachedExternalSevice;
}

StringBuffer output;
try
{
VStringBuffer getServiceCmd("kubectl get svc --selector=server=%s --output=jsonpath={.items[0].status.loadBalancer.ingress[0].hostname},{.items[0].status.loadBalancer.ingress[0].ip},{.items[0].spec.ports[0].port}", serviceName);
k8s::runKubectlCommand("get-external-service", getServiceCmd, nullptr, &output);
}
catch (IException *e)
{
EXCLOG(e);
VStringBuffer exceptionText("Failed to get external service for '%s'. Error: [%d, ", serviceName, e->errorCode());
e->errorMessage(exceptionText).append("]");
e->Release();
throw makeStringException(-1, exceptionText);
}
StringArray fields;
fields.appendList(output, ",");

// NB: add even if no result, want non-result to be cached too
std::string host, port;
if (fields.ordinality() == 3) // hostname,ip,port. NB: hostname may be missing, but still present as a blank field
{
host = fields.item(0); // hostname
if (0 == host.length())
host = fields.item(1); // ip
port = fields.item(2);
}
auto servicePair = std::make_pair(host, atoi(port.c_str()));
externalServiceCache.add(serviceName, servicePair);
return servicePair;
}

std::pair<std::string, unsigned> getDafileServiceFromConfig(const char *application)
{
#ifndef _CONTAINERIZED
UNIMPLEMENTED_X("getDafileServiceFromConfig");
#endif
/* NB: For now expect 1 dafilesrv in configuration only
* We could have multiple dafilesrv services with e.g. different specs./replicas etc. that
* serviced different planes. At the moment dafilesrv mounts all data planes.
*/
VStringBuffer serviceXPath("services[@type='%s']", application);
Owned<IPropertyTreeIterator> dafilesrvServices = getGlobalConfigSP()->getElements(serviceXPath);
if (!dafilesrvServices->first())
throw makeStringExceptionV(JLIBERR_K8sServiceError, "dafilesrv service '%s' not defined or disabled", application);
const IPropertyTree &dafilesrv = dafilesrvServices->query();
if (!dafilesrv.getPropBool("@public"))
throw makeStringExceptionV(JLIBERR_K8sServiceError, "dafilesrv service '%s' has no public service defined", application);
StringBuffer dafilesrvName;
dafilesrv.getProp("@name", dafilesrvName);
auto externalService = getExternalService(dafilesrvName);
if (externalService.first.empty())
throw makeStringExceptionV(JLIBERR_K8sServiceError, "dafilesrv service '%s' - external service '%s' not found", application, dafilesrvName.str());
if (0 == externalService.second)
throw makeStringExceptionV(JLIBERR_K8sServiceError, "dafilesrv service '%s' - external service '%s' port not defined", application, dafilesrvName.str());
return externalService;
}


static unsigned podInfoInitCBId = 0;
MODULE_INIT(INIT_PRIORITY_STANDARD)
{
Expand Down
7 changes: 7 additions & 0 deletions system/jlib/jcontainerized.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,12 @@ jlib_decl void waitJob(const char *componentName, const char *job, unsigned pend
jlib_decl bool applyYaml(const char *componentName, const char *wuid, const char *job, const char *resourceType, const std::list<std::pair<std::string, std::string>> &extraParams, bool optional, bool autoCleanup);
jlib_decl void runJob(const char *componentName, const char *wuid, const char *job, const std::list<std::pair<std::string, std::string>> &extraParams={});

extern jlib_decl void runKubectlCommand(const char *title, const char *cmd, const char *input, StringBuffer *output);

// return the k8s external host and port for serviceName
extern jlib_decl std::pair<std::string, unsigned> getExternalService(const char *serviceName);

extern jlib_decl std::pair<std::string, unsigned> getDafileServiceFromConfig(const char *application);


}
1 change: 1 addition & 0 deletions system/jlib/jerror.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#define JLIBERR_InternalError 6002
#define JLIBERR_CppCompileError 6003
#define JLIBERR_UnexpectedValue 6004
#define JLIBERR_K8sServiceError 6005

//---- Text for all errors (make it easy to internationalise) ---------------------------

Expand Down
94 changes: 0 additions & 94 deletions system/jlib/jmisc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1004,100 +1004,6 @@ jlib_decl char **getSystemEnv()
}


void runKubectlCommand(const char *title, const char *cmd, const char *input, StringBuffer *output)
{
#ifndef _CONTAINERIZED
UNIMPLEMENTED_X("runKubectlCommand");
#endif
// NB: will fire an exception if command fails (returns non-zero exit code)

StringBuffer _output, error;
if (!output)
output = &_output;
unsigned ret = runExternalCommand(title, *output, error, cmd, input, ".", nullptr);
if (output->length())
MLOG(MCdebugInfo, unknownJob, "%s: ret=%u, stdout=%s", cmd, ret, output->trimRight().str());
if (error.length())
MLOG(MCdebugError, unknownJob, "%s: ret=%u, stderr=%s", cmd, ret, error.trimRight().str());
if (ret)
{
if (input)
MLOG(MCdebugError, unknownJob, "Using input %s", input);
throw makeStringExceptionV(0, "Failed to run %s: error %u: %s", cmd, ret, error.str());
}
}

static CTimeLimitedCache<std::string, std::pair<std::string, unsigned>> externalServiceCache;
static CriticalSection externalServiceCacheCrit;
std::pair<std::string, unsigned> getExternalService(const char *serviceName)
{
#ifndef _CONTAINERIZED
UNIMPLEMENTED_X("getExternalService");
#endif
{
CriticalBlock b(externalServiceCacheCrit);
std::pair<std::string, unsigned> cachedExternalSevice;
if (externalServiceCache.get(serviceName, cachedExternalSevice))
return cachedExternalSevice;
}

StringBuffer output;
try
{
VStringBuffer getServiceCmd("kubectl get svc --selector=server=%s --output=jsonpath={.items[0].status.loadBalancer.ingress[0].hostname},{.items[0].status.loadBalancer.ingress[0].ip},{.items[0].spec.ports[0].port}", serviceName);
runKubectlCommand("get-external-service", getServiceCmd, nullptr, &output);
}
catch (IException *e)
{
EXCLOG(e);
VStringBuffer exceptionText("Failed to get external service for '%s'. Error: [%d, ", serviceName, e->errorCode());
e->errorMessage(exceptionText).append("]");
e->Release();
throw makeStringException(-1, exceptionText);
}
StringArray fields;
fields.appendList(output, ",");

// NB: add even if no result, want non-result to be cached too
std::string host, port;
if (fields.ordinality() == 3) // hostname,ip,port. NB: hostname may be missing, but still present as a blank field
{
host = fields.item(0); // hostname
if (0 == host.length())
host = fields.item(1); // ip
port = fields.item(2);
}
auto servicePair = std::make_pair(host, atoi(port.c_str()));
externalServiceCache.add(serviceName, servicePair);
return servicePair;
}

std::pair<std::string, unsigned> getDafileServiceFromConfig(const char *application)
{
#ifndef _CONTAINERIZED
UNIMPLEMENTED_X("getDafileServiceFromConfig");
#endif
/* NB: For now expect 1 dafilesrv in configuration only
* We could have multiple dafilesrv services with e.g. different specs./replicas etc. that
* serviced different planes. At the moment dafilesrv mounts all data planes.
*/
VStringBuffer serviceXPath("services[@type='%s']", application);
Owned<IPropertyTreeIterator> dafilesrvServices = getGlobalConfigSP()->getElements(serviceXPath);
if (!dafilesrvServices->first())
throw makeStringException(-1, "dafilesrv service not defined");
const IPropertyTree &dafilesrv = dafilesrvServices->query();
if (!dafilesrv.getPropBool("@public"))
throw makeStringException(-1, "dafilesrv service has no public service defined");
StringBuffer dafilesrvName;
dafilesrv.getProp("@name", dafilesrvName);
auto externalService = getExternalService(dafilesrvName);
if (externalService.first.empty())
throw makeStringExceptionV(-1, "dafilesrv '%s': external service not found", dafilesrvName.str());
if (0 == externalService.second)
throw makeStringExceptionV(-1, "dafilesrv '%s': external service port not defined", dafilesrvName.str());
return externalService;
}

// checks if 'name' is an internal environment variable (prefixed with 'HPCC_')
// if !matches : returns null
// if matches : returns allocated copy of configured value or defaultValue if not set.
Expand Down
8 changes: 0 additions & 8 deletions system/jlib/jmisc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,14 +332,6 @@ extern jlib_decl char *mkdtemp(char *_template);

extern jlib_decl char **getSystemEnv();


extern jlib_decl void runKubectlCommand(const char *title, const char *cmd, const char *input, StringBuffer *output);

// return the k8s external host and port for serviceName
extern jlib_decl std::pair<std::string, unsigned> getExternalService(const char *serviceName);

extern jlib_decl std::pair<std::string, unsigned> getDafileServiceFromConfig(const char *application);

extern jlib_decl char *getHPCCEnvVal(const char *name, const char *defaultValue);

#endif
Loading