From da6d8e5945d712baee02e5b80975c7894a33b646 Mon Sep 17 00:00:00 2001 From: Tim Pavlik Date: Tue, 6 May 2025 11:32:23 -0700 Subject: [PATCH 1/5] Add logic to pick a single search head when search head cluster is detected. --- out/notebooks/controller.ts | 23 +++++++-- out/notebooks/spl2/controller.ts | 9 ++-- out/notebooks/splunk.ts | 86 +++++++++++++++++++++++++------- 3 files changed, 91 insertions(+), 27 deletions(-) diff --git a/out/notebooks/controller.ts b/out/notebooks/controller.ts index 6246d77..3d1eb04 100644 --- a/out/notebooks/controller.ts +++ b/out/notebooks/controller.ts @@ -4,6 +4,7 @@ import { cancelSearchJob, createSearchJob, getClient, + getSearchHeadClusterMemberClient, getSearchJob, getSearchJobResults, wait, @@ -18,6 +19,7 @@ export class SplunkController { protected supportedLanguages: string[]; protected _controller: vscode.NotebookController; + protected _service; // Splunk Javascript SDK Client private _executionOrder = 0; private _interrupted = false; private _tokens = {}; @@ -55,6 +57,8 @@ export class SplunkController { this.notebookType, this.label ); + // Attempt to connect to individual search head if part of a search head cluster + this.refreshService(); this._controller.supportedLanguages = this.supportedLanguages; this._controller.supportsExecutionOrder = true; @@ -70,6 +74,20 @@ export class SplunkController { this._execute([cell], notebookDocument, this._controller); } + async refreshService() { + const config = vscode.workspace.getConfiguration(); + const restUrl = config.get('splunk.commands.splunkRestUrl'); + const token = config.get('splunk.commands.token'); + // Create a new SDK client if one hasn't been created or token / url settings have been changed + if (!this._service || (this._service._originalURL !== restUrl) || (this._service.sessionKey !== token)) { + this._service = getClient(); + // Check to see if the splunk deployment is part of a search head cluster, choose a single search head + // to target if so to ensure that adhoc jobs are immediately available (without replication) + const newService = await getSearchHeadClusterMemberClient(this._service); + this._service = newService; + } + } + protected _execute( cells: vscode.NotebookCell[], _notebook: vscode.NotebookDocument, @@ -145,9 +163,8 @@ export class SplunkController { let query = cell.document.getText().trim().replace(/^\s+|\s+$/g, ''); - const service = getClient() - - let jobs = service.jobs(); + await this.refreshService(); + let jobs = this._service.jobs(); const tokenRegex = /\$([a-zA-Z0-9_.|]*?)\$/g; diff --git a/out/notebooks/spl2/controller.ts b/out/notebooks/spl2/controller.ts index 17b0259..0cb71ff 100644 --- a/out/notebooks/spl2/controller.ts +++ b/out/notebooks/spl2/controller.ts @@ -1,9 +1,6 @@ import * as vscode from 'vscode'; -import { - dispatchSpl2Module, - getClient, -} from '../splunk'; +import { dispatchSpl2Module } from '../splunk'; import { SplunkController } from '../controller'; import { splunkMessagesToOutputItems } from '../utils/messages'; import { getAppSubNamespace } from './serializer'; @@ -30,7 +27,6 @@ export class Spl2Controller extends SplunkController { const execution = super._startExecution(cell); const spl2Module = cell.document.getText().trim(); - const service = getClient(); let fullNamespace: string = cell?.metadata?.splunk?.namespace || ''; // Get apps.[.optional.sub.namespaces] from fullNamespace const [app, subNamespace] = getAppSubNamespace(fullNamespace); @@ -39,8 +35,9 @@ export class Spl2Controller extends SplunkController { let job; try { + await this.refreshService(); job = await dispatchSpl2Module( - service, + this._service, spl2Module, app, subNamespace, diff --git a/out/notebooks/splunk.ts b/out/notebooks/splunk.ts index 3929584..665b53c 100644 --- a/out/notebooks/splunk.ts +++ b/out/notebooks/splunk.ts @@ -9,27 +9,25 @@ export function getClient() { const restUrl = config.get('splunk.commands.splunkRestUrl'); const token = config.get('splunk.commands.token'); - let url = new URL(restUrl); + const url = new URL(restUrl); const scheme = url.protocol.replace(':', ''); const port = url.port || (scheme === 'https' ? '443' : '80'); const host = url.hostname; - let service = new splunk.Service({ + const service = new splunk.Service({ scheme: scheme, host: host, port: port, sessionKey: token, - version: '8', authorization: 'Bearer', }); + service._originalURL = restUrl; return service; } export function splunkLogin(service) { - return new Promise(function(resolve, reject) { - service.login(function(err, wasSuccessful) { if (err !== null || !wasSuccessful) { reject(err); @@ -37,10 +35,7 @@ export function splunkLogin(service) { resolve(null); } }); - }); - - } @@ -53,7 +48,6 @@ export function createSearchJob(jobs, query, options) { resolve(data); } }); - }); } @@ -69,6 +63,61 @@ function makeHeaders(service: any): object { }; } +/** + * Check to see if the SDK client is part of a search head cluster, if so return a new + * client pointing to an individual search head member, such that any search ids created + * will be immediately available for results rather than waiting for artifact replication + * across the search head cluster. + * @param service Instance of the Javascript SDK Service + * + * @returns Promise + */ +export function getSearchHeadClusterMemberClient(service: any): Promise { + const shcUrl = `${service.prefix}/services/shcluster/member/members?output_mode=json`; + console.log(`Attempting to determine SHC info if present using ${shcUrl}`); + return needle( + "GET", + shcUrl, + { + 'headers': makeHeaders(service), + 'followAllRedirects': true, + 'timeout': 0, + 'strictSSL': false, + 'rejectUnauthorized' : false, + }) + .then((response) => { + console.log(`Response from shcUrl status code: ${response.statusCode}`); + console.log(`Response from shcUrl body: \n'${JSON.stringify(response.body)}'`); + const data = response.body; + if (response.statusCode >= 400 || + !Object.prototype.isPrototypeOf(data) + || data.entry === undefined + || !Array.isArray(data.entry) + || data.entry.length === 0 + || data.entry[0].content === undefined + || data.entry[0].content.mgmt_uri === undefined + ) { + console.warn("Unsuccessful response from /services/shcluster/member/members endpoint encountered, reverting to original service client.") + return service; + } + // This is in the expected successful response format + vscode.window.showInformationMessage(`Discovered search head cluster members. Attempting to communicate directly with SH ${data.entry[0].content.mgmt_uri}`); + const url = new URL(data.entry[0].content.mgmt_uri); + const scheme = url.protocol.replace(':', ''); + const port = url.port || (scheme === 'https' ? '443' : '80'); + const host = url.hostname; + const newService = new splunk.Service({ + scheme: scheme, + host: host, + port: port, + sessionKey: service.sessionKey, + authorization: 'Bearer', + }); + newService._originalURL = service._originalURL; + return newService; + }); +} + /** * Update a module by calling the PUT /services/spl2/modules/. * @param service Instance of the Javascript SDK Service @@ -77,7 +126,7 @@ function makeHeaders(service: any): object { * @param module Full contents of the module to update with * @returns Promise (or throw Error containing data.messages[]) */ -export function updateSpl2Module(service: any, moduleName: string, namespace: string, module: string) { +export function updateSpl2Module(service: any, moduleName: string, namespace: string, module: string): Promise { // The Splunk SDK for Javascript doesn't currently support the spl2/modules endpoints // nor does it support sending requests in JSON format (only receiving responses), so // for now use the underlying needle library that the SDK uses for requests/responses @@ -131,9 +180,9 @@ export function updateSpl2Module(service: any, moduleName: string, namespace: st * @param namespace Namespace _within_ the apps. to run, this will be used directly in the body of the request * @param earliest Earliest time to be included in the body of the request * @param latest Latest time to be included in the body of the request - * @returns A Promise containing the job id created (or throw an Error containing data.messages[]) + * @returns A Promise containing the job information including sid created (or throw an Error containing data.messages[]) */ -export function dispatchSpl2Module(service: any, spl2Module: string, app: string, namespace: string, earliest: string, latest: string) { +export function dispatchSpl2Module(service: any, spl2Module: string, app: string, namespace: string, earliest: string, latest: string): Promise { // For now we're using /services/ which doesn't respect relative namespaces, // so for now hardcode this to '' but if/when /servicesNS/ namespace = ''; @@ -194,6 +243,7 @@ export function dispatchSpl2Module(service: any, spl2Module: string, app: string .then((response) => { console.log(`Response status code: ${response.statusCode}`); console.log(`Response body: \n'${JSON.stringify(response.body)}'`); + console.log(`Response headers: \n'${JSON.stringify(response.headers)}'`); const data = response.body; if (response.statusCode >= 400 || !Array.prototype.isPrototypeOf(data) || data.length < 1) { handleErrorPayloads(data, response.statusCode); @@ -267,7 +317,7 @@ function handleErrorPayloads(data: any, statusCode: number) { }); } -export function getSearchJobBySid(service, sid) { +export function getSearchJobBySid(service, sid): Promise { return new Promise(function(resolve, reject) { service.getJob(sid, function(err, data) { if (err != null) { @@ -280,7 +330,7 @@ export function getSearchJobBySid(service, sid) { } -export function getSearchJob(job) { +export function getSearchJob(job): Promise { return new Promise(function(resolve, reject) { job.fetch(function(err, job) { if (err !== null) { @@ -293,7 +343,7 @@ export function getSearchJob(job) { }); } -export function getJobSearchLog(job) { +export function getJobSearchLog(job): Promise { return new Promise(function(resolve, reject) { job.searchlog(function(err, log) { if (err !== null) { @@ -306,7 +356,7 @@ export function getJobSearchLog(job) { }); } -export function getSearchJobResults(job) { +export function getSearchJobResults(job): Promise { return new Promise(function(resolve, reject) { job.get("results", {"output_mode": "json_cols"},function(err, results) { if (err !== null) { @@ -319,7 +369,7 @@ export function getSearchJobResults(job) { }); } -export function cancelSearchJob(job) { +export function cancelSearchJob(job): Promise { return new Promise(function(resolve, reject) { job.cancel(function(err, results) { if (err !== null) { @@ -332,7 +382,7 @@ export function cancelSearchJob(job) { }); } -export function wait(ms = 1000) { +export function wait(ms = 1000): Promise { return new Promise(resolve => { setTimeout(resolve, ms); }); From 6612d5326411f4130133dd502aee1871c71bf282 Mon Sep 17 00:00:00 2001 From: Tim Pavlik Date: Tue, 6 May 2025 17:47:01 -0700 Subject: [PATCH 2/5] Fix to SDK usage --- out/notebooks/splunk.ts | 110 +++++++++++----------------------------- package-lock.json | 16 +++--- package.json | 2 +- 3 files changed, 39 insertions(+), 89 deletions(-) diff --git a/out/notebooks/splunk.ts b/out/notebooks/splunk.ts index 665b53c..4617c32 100644 --- a/out/notebooks/splunk.ts +++ b/out/notebooks/splunk.ts @@ -9,12 +9,12 @@ export function getClient() { const restUrl = config.get('splunk.commands.splunkRestUrl'); const token = config.get('splunk.commands.token'); - const url = new URL(restUrl); + let url = new URL(restUrl); const scheme = url.protocol.replace(':', ''); const port = url.port || (scheme === 'https' ? '443' : '80'); const host = url.hostname; - const service = new splunk.Service({ + let service = new splunk.Service({ scheme: scheme, host: host, port: port, @@ -27,28 +27,13 @@ export function getClient() { } export function splunkLogin(service) { - return new Promise(function(resolve, reject) { - service.login(function(err, wasSuccessful) { - if (err !== null || !wasSuccessful) { - reject(err); - } else { - resolve(null); - } - }); - }); + let request = service.login(); + return request; } - export function createSearchJob(jobs, query, options) { - return new Promise(function(resolve, reject) { - jobs.create(query, options, function(err, data) { - if (err !== null) { - reject(err); - } else { - resolve(data); - } - }); - }); + let request = jobs.create(query, options); + return request; } /** @@ -72,7 +57,7 @@ function makeHeaders(service: any): object { * * @returns Promise */ -export function getSearchHeadClusterMemberClient(service: any): Promise { +export function getSearchHeadClusterMemberClient(service: any) { const shcUrl = `${service.prefix}/services/shcluster/member/members?output_mode=json`; console.log(`Attempting to determine SHC info if present using ${shcUrl}`); return needle( @@ -126,7 +111,7 @@ export function getSearchHeadClusterMemberClient(service: any): Promise { * @param module Full contents of the module to update with * @returns Promise (or throw Error containing data.messages[]) */ -export function updateSpl2Module(service: any, moduleName: string, namespace: string, module: string): Promise { +export function updateSpl2Module(service: any, moduleName: string, namespace: string, module: string) { // The Splunk SDK for Javascript doesn't currently support the spl2/modules endpoints // nor does it support sending requests in JSON format (only receiving responses), so // for now use the underlying needle library that the SDK uses for requests/responses @@ -180,9 +165,9 @@ export function updateSpl2Module(service: any, moduleName: string, namespace: st * @param namespace Namespace _within_ the apps. to run, this will be used directly in the body of the request * @param earliest Earliest time to be included in the body of the request * @param latest Latest time to be included in the body of the request - * @returns A Promise containing the job information including sid created (or throw an Error containing data.messages[]) + * @returns A Promise containing the job id created (or throw an Error containing data.messages[]) */ -export function dispatchSpl2Module(service: any, spl2Module: string, app: string, namespace: string, earliest: string, latest: string): Promise { +export function dispatchSpl2Module(service: any, spl2Module: string, app: string, namespace: string, earliest: string, latest: string) { // For now we're using /services/ which doesn't respect relative namespaces, // so for now hardcode this to '' but if/when /servicesNS/ namespace = ''; @@ -252,6 +237,10 @@ export function dispatchSpl2Module(service: any, spl2Module: string, app: string // This is in the expected successful response format const sid = data[0]['sid']; return getSearchJobBySid(service, sid); + }) + .then((info) => { + console.log(`Retrieved job info: ${JSON.stringify(info)}`); + return info; }); } @@ -317,72 +306,33 @@ function handleErrorPayloads(data: any, statusCode: number) { }); } -export function getSearchJobBySid(service, sid): Promise { - return new Promise(function(resolve, reject) { - service.getJob(sid, function(err, data) { - if (err != null) { - reject(err); - } else { - resolve(data); - } - }); - }); +export function getSearchJobBySid(service, sid) { + let request = service.getJob(sid); + return request; } -export function getSearchJob(job): Promise { - return new Promise(function(resolve, reject) { - job.fetch(function(err, job) { - if (err !== null) { - reject(err); - } else { - resolve(job); - } - }); - - }); +export function getSearchJob(job) { + let request = job.fetch(); + return request; } -export function getJobSearchLog(job): Promise { - return new Promise(function(resolve, reject) { - job.searchlog(function(err, log) { - if (err !== null) { - reject(err); - } else { - resolve(log); - } - }); - - }); +export function getJobSearchLog(job) { + let request = job.searchlog(); + return request; } -export function getSearchJobResults(job): Promise { - return new Promise(function(resolve, reject) { - job.get("results", {"output_mode": "json_cols"},function(err, results) { - if (err !== null) { - reject(err); - } else { - resolve(results); - } - }); - - }); +export function getSearchJobResults(job) { + let request = job.get("results", {"output_mode": "json_cols"}); + return request; } -export function cancelSearchJob(job): Promise { - return new Promise(function(resolve, reject) { - job.cancel(function(err, results) { - if (err !== null) { - reject(err); - } else { - resolve(results); - } - - }); - }); +export function cancelSearchJob(job) { + let request = job.cancel(); + return request; } -export function wait(ms = 1000): Promise { +export function wait(ms = 1000) { return new Promise(resolve => { setTimeout(resolve, ms); }); diff --git a/package-lock.json b/package-lock.json index 37ac5d8..18921e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "querystring-es3": "^0.2.1", "react": "^16.14.0", "react-dom": "^16.14.0", - "splunk-sdk": "^2.0.0", + "splunk-sdk": "^2.0.2", "styled-components": "^5.1.1", "tar-fs": "^2.1.1", "ts-loader": "^9.4.2", @@ -6158,9 +6158,9 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.0.tgz", + "integrity": "sha512-qCf+V4dtlNhSRXGAZatc1TasyFO6GjohcOul807YOb5ik3+kQSnb4d7iajeCL8QHaJ4uZEjCgiCJerKXwdRVlQ==", "engines": { "node": ">= 0.6" } @@ -11954,11 +11954,11 @@ } }, "node_modules/splunk-sdk": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/splunk-sdk/-/splunk-sdk-2.0.0.tgz", - "integrity": "sha512-xmkoZ1I6KxQrs7B15db5nUqLGDR+ohDZ21/sGfcLRwOYfTCGR8uHVP/huEwTtrSXJ/WcgyevDwnIniOjuKhcOQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/splunk-sdk/-/splunk-sdk-2.0.2.tgz", + "integrity": "sha512-25S9b1Bjcsl9cI7jFHf12rDRzwx7BukM9uiC4/m2n9Vp3NwM6szKJwezX9Nquq6uCUo6d8PFqGZeWAOajNwQdg==", "dependencies": { - "cookie": "0.4.2", + "cookie": "0.7.0", "dotenv": "16.0.0", "elementtree": "0.1.7", "needle": "3.0.0" diff --git a/package.json b/package.json index de69ef0..a26a5fa 100644 --- a/package.json +++ b/package.json @@ -460,7 +460,7 @@ "querystring-es3": "^0.2.1", "react": "^16.14.0", "react-dom": "^16.14.0", - "splunk-sdk": "^2.0.0", + "splunk-sdk": "^2.0.2", "styled-components": "^5.1.1", "tar-fs": "^2.1.1", "ts-loader": "^9.4.2", From 4bda67da4524ca24610d66da6a765fce5741742c Mon Sep 17 00:00:00 2001 From: Tim Pavlik Date: Tue, 6 May 2025 17:49:39 -0700 Subject: [PATCH 3/5] Clean up function signatures --- out/notebooks/splunk.ts | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/out/notebooks/splunk.ts b/out/notebooks/splunk.ts index 4617c32..90a520a 100644 --- a/out/notebooks/splunk.ts +++ b/out/notebooks/splunk.ts @@ -4,7 +4,7 @@ import * as vscode from 'vscode'; import { SplunkMessage } from './utils/messages'; import { getModuleStatements } from './utils/parsing'; -export function getClient() { +export function getClient(): any { const config = vscode.workspace.getConfiguration(); const restUrl = config.get('splunk.commands.splunkRestUrl'); const token = config.get('splunk.commands.token'); @@ -26,12 +26,12 @@ export function getClient() { return service; } -export function splunkLogin(service) { +export function splunkLogin(service): Promise { let request = service.login(); return request; } -export function createSearchJob(jobs, query, options) { +export function createSearchJob(jobs, query, options): Promise { let request = jobs.create(query, options); return request; } @@ -57,7 +57,7 @@ function makeHeaders(service: any): object { * * @returns Promise */ -export function getSearchHeadClusterMemberClient(service: any) { +export function getSearchHeadClusterMemberClient(service: any): Promise { const shcUrl = `${service.prefix}/services/shcluster/member/members?output_mode=json`; console.log(`Attempting to determine SHC info if present using ${shcUrl}`); return needle( @@ -111,7 +111,7 @@ export function getSearchHeadClusterMemberClient(service: any) { * @param module Full contents of the module to update with * @returns Promise (or throw Error containing data.messages[]) */ -export function updateSpl2Module(service: any, moduleName: string, namespace: string, module: string) { +export function updateSpl2Module(service: any, moduleName: string, namespace: string, module: string): Promise { // The Splunk SDK for Javascript doesn't currently support the spl2/modules endpoints // nor does it support sending requests in JSON format (only receiving responses), so // for now use the underlying needle library that the SDK uses for requests/responses @@ -165,9 +165,9 @@ export function updateSpl2Module(service: any, moduleName: string, namespace: st * @param namespace Namespace _within_ the apps. to run, this will be used directly in the body of the request * @param earliest Earliest time to be included in the body of the request * @param latest Latest time to be included in the body of the request - * @returns A Promise containing the job id created (or throw an Error containing data.messages[]) + * @returns A Promise containing the job information including sid created (or throw an Error containing data.messages[]) */ -export function dispatchSpl2Module(service: any, spl2Module: string, app: string, namespace: string, earliest: string, latest: string) { +export function dispatchSpl2Module(service: any, spl2Module: string, app: string, namespace: string, earliest: string, latest: string): Promise { // For now we're using /services/ which doesn't respect relative namespaces, // so for now hardcode this to '' but if/when /servicesNS/ namespace = ''; @@ -237,10 +237,6 @@ export function dispatchSpl2Module(service: any, spl2Module: string, app: string // This is in the expected successful response format const sid = data[0]['sid']; return getSearchJobBySid(service, sid); - }) - .then((info) => { - console.log(`Retrieved job info: ${JSON.stringify(info)}`); - return info; }); } @@ -306,33 +302,33 @@ function handleErrorPayloads(data: any, statusCode: number) { }); } -export function getSearchJobBySid(service, sid) { +export function getSearchJobBySid(service, sid): Promise { let request = service.getJob(sid); return request; } -export function getSearchJob(job) { +export function getSearchJob(job): Promise { let request = job.fetch(); return request; } -export function getJobSearchLog(job) { +export function getJobSearchLog(job): Promise { let request = job.searchlog(); return request; } -export function getSearchJobResults(job) { +export function getSearchJobResults(job): Promise { let request = job.get("results", {"output_mode": "json_cols"}); return request; } -export function cancelSearchJob(job) { +export function cancelSearchJob(job): Promise { let request = job.cancel(); return request; } -export function wait(ms = 1000) { +export function wait(ms = 1000): Promise { return new Promise(resolve => { setTimeout(resolve, ms); }); From 6adf16afb1eaf3ba14416f7e3ad7ffdb20b6d521 Mon Sep 17 00:00:00 2001 From: Tim Pavlik Date: Thu, 8 May 2025 09:30:14 -0700 Subject: [PATCH 4/5] Remove unused function --- out/notebooks/splunk.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/out/notebooks/splunk.ts b/out/notebooks/splunk.ts index 90a520a..b3fb1f8 100644 --- a/out/notebooks/splunk.ts +++ b/out/notebooks/splunk.ts @@ -26,11 +26,6 @@ export function getClient(): any { return service; } -export function splunkLogin(service): Promise { - let request = service.login(); - return request; -} - export function createSearchJob(jobs, query, options): Promise { let request = jobs.create(query, options); return request; From 4c7bd7db1f9c6eb48bacc9da62b2cc45bf8e49b1 Mon Sep 17 00:00:00 2001 From: Tim Pavlik Date: Thu, 8 May 2025 15:13:42 -0700 Subject: [PATCH 5/5] Check for undefined/null client and add try/catch for retrieving shc info --- out/notebooks/controller.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/out/notebooks/controller.ts b/out/notebooks/controller.ts index 3d1eb04..012a877 100644 --- a/out/notebooks/controller.ts +++ b/out/notebooks/controller.ts @@ -79,12 +79,21 @@ export class SplunkController { const restUrl = config.get('splunk.commands.splunkRestUrl'); const token = config.get('splunk.commands.token'); // Create a new SDK client if one hasn't been created or token / url settings have been changed - if (!this._service || (this._service._originalURL !== restUrl) || (this._service.sessionKey !== token)) { + if ((this._service === undefined) + || (this._service === null) + || (this._service._originalURL !== restUrl) + || (this._service.sessionKey !== token) + ) { this._service = getClient(); // Check to see if the splunk deployment is part of a search head cluster, choose a single search head // to target if so to ensure that adhoc jobs are immediately available (without replication) - const newService = await getSearchHeadClusterMemberClient(this._service); - this._service = newService; + try { + const newService = await getSearchHeadClusterMemberClient(this._service); + this._service = newService; + } catch (err) { + console.warn(`Error retrieving search head cluster information:`); + console.warn(err); + } } }