From 7b2a6bcef550e1b6d287ed5f382caf298fea032f Mon Sep 17 00:00:00 2001 From: Emmanuel Yusufu Kimaswa Date: Thu, 28 May 2026 15:00:06 +0300 Subject: [PATCH] fix: pass kubernetesPath into the mustache path context The kubernetes command paths fall back to {{kubernetesPath}} when no mount_point is given, but kubernetesPath only lives on the client and was never added to the mustache render context, so the templates rendered an empty segment (e.g. kubernetesLogin hit /auth//login instead of /auth/kubernetes/login). Inject client.kubernetesPath into the render context; per-call args still take precedence. Adds unit tests for the default and a custom kubernetesPath. Closes #300 --- src/index.js | 5 ++-- test/unit.js | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 491ee4b..314f161 100644 --- a/src/index.js +++ b/src/index.js @@ -341,8 +341,9 @@ module.exports = (config = {}) => { client[name] = (args = {}) => { const options = { ...config.requestOptions, ...args.requestOptions }; options.method = conf.method; - // replace args in path. - options.path = mustache.render(conf.path, args); + // replace args in path. kubernetesPath lives on the client (not in + // args), so inject it into the template context; args still win. + options.path = mustache.render(conf.path, { kubernetesPath: client.kubernetesPath, ...args }); // no schema object -> no validation if (!conf.schema) { if (options.method === 'POST' || options.method === 'PUT') { diff --git a/test/unit.js b/test/unit.js index 8ba39df..67906db 100644 --- a/test/unit.js +++ b/test/unit.js @@ -143,6 +143,72 @@ describe('node-vault', () => { }) .catch(done); }); + + it('should default the kubernetes mount point in kubernetesLogin path', (done) => { + const request = sinon.stub(); + const response = sinon.stub(); + response.statusCode = 200; + response.body = { auth: { client_token: 'test-token' } }; + request.returns({ + then(fn) { + return fn(response); + }, + catch(fn) { + return fn(); + }, + }); + + const vault = index({ + endpoint: 'http://localhost:8200', + token: '123', + 'request-promise': { + defaults: () => request, + }, + }); + + vault.kubernetesLogin({ role: 'my-role', jwt: 'my-jwt' }) + .then(() => { + request.should.have.calledOnce(); + const uri = request.firstCall.args[0].uri; + uri.should.equal('http://localhost:8200/v1/auth/kubernetes/login'); + uri.should.not.contain('/auth//login'); + done(); + }) + .catch(done); + }); + + it('should use a custom kubernetesPath in kubernetesLogin path', (done) => { + const request = sinon.stub(); + const response = sinon.stub(); + response.statusCode = 200; + response.body = { auth: { client_token: 'test-token' } }; + request.returns({ + then(fn) { + return fn(response); + }, + catch(fn) { + return fn(); + }, + }); + + const vault = index({ + endpoint: 'http://localhost:8200', + token: '123', + kubernetesPath: 'k8s-prod', + 'request-promise': { + defaults: () => request, + }, + }); + + vault.kubernetesLogin({ role: 'my-role', jwt: 'my-jwt' }) + .then(() => { + request.should.have.calledOnce(); + const uri = request.firstCall.args[0].uri; + uri.should.equal('http://localhost:8200/v1/auth/k8s-prod/login'); + done(); + }) + .catch(done); + }); }); describe('client', () => {