Skip to content

Commit 9a7362c

Browse files
authored
Merge pull request #134 from glennsarti/use-pdk-ruby-environment
(GH-130) Prepare to use PDK ruby if puppet agent not available
2 parents d9b2280 + 9b954d8 commit 9a7362c

File tree

1 file changed

+182
-42
lines changed

1 file changed

+182
-42
lines changed

client/src/connection.ts

Lines changed: 182 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,31 @@ export class ConnectionManager implements IConnectionManager {
209209
callback(proc);
210210
}
211211

212-
private createLanguageServerProcess(serverExe: string, callback : Function) {
213-
this.logger.debug('Language server found at: ' + serverExe)
212+
private getDirectories(parent) {
213+
return fs.readdirSync(parent).filter(function (file) {
214+
return fs.statSync(path.join(parent, file)).isDirectory();
215+
});
216+
}
214217

215-
let cmd: string = undefined;
216-
let args = [serverExe];
217-
let options : cp.SpawnOptions = {};
218+
private pathEnvSeparator() {
219+
if (process.platform == 'win32') {
220+
return ";";
221+
} else {
222+
return ":";
223+
}
224+
}
225+
226+
private getLanguageServerFromPuppetAgent(serverExe) {
227+
let logPrefix: string = '[getLanguageServerFromPuppetAgent] ';
228+
// setup defaults
229+
let spawn_options: cp.SpawnOptions = {}
230+
spawn_options.env = process.env;
231+
let result = {
232+
command: 'ruby',
233+
args: [serverExe],
234+
options: spawn_options,
235+
}
236+
let puppetAgentDir: string = null;
218237

219238
// type Platform = 'aix'
220239
// | 'android'
@@ -234,69 +253,190 @@ export class ConnectionManager implements IConnectionManager {
234253
comspec = path.join(process.env["WINDIR"],"sysnative","cmd.exe");
235254
programFiles = process.env["ProgramW6432"];
236255
}
237-
let puppetDir: string = undefined;
256+
238257
if (this.connectionConfiguration.puppetAgentDir == undefined) {
239-
puppetDir = path.join(programFiles, "Puppet Labs", "Puppet");
258+
puppetAgentDir = path.join(programFiles, "Puppet Labs", "Puppet");
240259
} else {
241-
puppetDir = this.connectionConfiguration.puppetAgentDir;
242-
}
243-
let environmentBat : string = path.join(puppetDir,"bin","environment.bat")
244-
245-
if (!fs.existsSync(puppetDir)) {
246-
this.setSessionFailure("Could not find Puppet Agent at " + puppetDir);
247-
vscode.window.showWarningMessage('Could not find Puppet Agent installed at "' + puppetDir + '". Functionality will be limited to syntax highlighting');
248-
return;
260+
puppetAgentDir = this.connectionConfiguration.puppetAgentDir;
249261
}
250262

251-
cmd = comspec;
252-
args = ['/K','CALL',environmentBat,'&&','ruby.exe',serverExe]
253-
options = {
254-
env: process.env,
255-
stdio: 'pipe',
256-
};
263+
result.options.stdio = 'pipe';
257264
break;
258265
default:
259-
this.logger.debug('Starting language server')
260-
261-
let rubyPath: string = undefined;
262266
if (this.connectionConfiguration.puppetAgentDir == undefined) {
263-
rubyPath = '/opt/puppetlabs/puppet/bin/ruby';
267+
puppetAgentDir = '/opt/puppetlabs/puppet';
264268
} else {
265-
rubyPath = path.join(this.connectionConfiguration.puppetAgentDir, "bin", "ruby");
269+
puppetAgentDir = this.connectionConfiguration.puppetAgentDir;
266270
}
267-
if (fs.existsSync(rubyPath)) { cmd = rubyPath }
268-
269-
// Default to ruby on the path
270-
if (cmd == undefined) { cmd = 'ruby' }
271-
options = {
272-
shell: true,
273-
env: process.env,
274-
stdio: 'pipe',
275-
};
271+
272+
result.options.stdio = 'pipe';
273+
result.options.shell = true;
274+
break;
275+
}
276+
// Check if this really is a Puppet Agent installation
277+
if (!fs.existsSync(path.join(puppetAgentDir, "VERSION"))) {
278+
this.logger.debug(logPrefix + "Could not find a valid Puppet Agent installation at " + puppetAgentDir);
279+
return null;
280+
} else {
281+
this.logger.debug(logPrefix + "Found a valid Puppet Agent installation at " + puppetAgentDir);
276282
}
277283

278-
if (cmd == undefined) {
279-
this.setSessionFailure("Unable to start the Language Server on this platform");
280-
vscode.window.showWarningMessage('The Puppet Language Server is not supported on this platform (' + process.platform + '). Functionality will be limited to syntax highlighting');
284+
let puppetDir = path.join(puppetAgentDir,"puppet");
285+
let facterDir = path.join(puppetAgentDir,"facter");
286+
let hieraDir = path.join(puppetAgentDir,"hiera");
287+
let mcoDir = path.join(puppetAgentDir,"mcollective");
288+
let rubydir = path.join(puppetAgentDir,"sys","ruby");
289+
let rubylib = path.join(puppetDir,"lib") + this.pathEnvSeparator() + path.join(facterDir,"lib") + this.pathEnvSeparator() + path.join(hieraDir,"lib") + this.pathEnvSeparator() + path.join(mcoDir,"lib")
290+
291+
if (process.platform == 'win32') {
292+
// Translate all slashes to / style to avoid puppet/ruby issue #11930
293+
rubylib = rubylib.replace(/\\/g,"/");
294+
}
295+
296+
// Setup the process environment variables
297+
if (result.options.env.PATH == undefined) { result.options.env.PATH = ''; }
298+
if (result.options.env.RUBYLIB == undefined) { result.options.env.RUBYLIB = ''; }
299+
result.options.env.RUBY_DIR = rubydir;
300+
result.options.env.PATH = path.join(puppetDir,"bin") + this.pathEnvSeparator() + path.join(facterDir,"bin") + this.pathEnvSeparator() + path.join(hieraDir,"bin") + this.pathEnvSeparator() + path.join(mcoDir,"bin") +
301+
this.pathEnvSeparator() + path.join(puppetAgentDir,"bin") + this.pathEnvSeparator() + path.join(rubydir,"bin") + this.pathEnvSeparator() + path.join(puppetAgentDir,"sys","tools","bin") +
302+
this.pathEnvSeparator() + result.options.env.PATH;
303+
result.options.env.RUBYLIB = rubylib + this.pathEnvSeparator() + result.options.env.RUBYLIB;
304+
result.options.env.RUBYOPT = 'rubygems';
305+
result.options.env.SSL_CERT_FILE = path.join(puppetDir,"ssl","cert.pem");
306+
result.options.env.SSL_CERT_DIR = path.join(puppetDir,"ssl","certs");
307+
308+
this.logger.debug(logPrefix + "Using environment variable RUBY_DIR=" + result.options.env.RUBY_DIR);
309+
this.logger.debug(logPrefix + "Using environment variable PATH=" + result.options.env.PATH);
310+
this.logger.debug(logPrefix + "Using environment variable RUBYLIB=" + result.options.env.RUBYLIB);
311+
this.logger.debug(logPrefix + "Using environment variable RUBYOPT=" + result.options.env.RUBYOPT);
312+
this.logger.debug(logPrefix + "Using environment variable SSL_CERT_FILE=" + result.options.env.SSL_CERT_FILE);
313+
this.logger.debug(logPrefix + "Using environment variable SSL_CERT_DIR=" + result.options.env.SSL_CERT_DIR);
314+
315+
return result;
316+
}
317+
318+
// Commented out for the moment. This will be enabled once the configuration and
319+
// exact user story is figured out.
320+
//
321+
// private getLanguageServerFromPDK(serverExe) {
322+
// let logPrefix: string = '[getLanguageServerFromPDK] ';
323+
// // setup defaults
324+
// let spawn_options: cp.SpawnOptions = {}
325+
// spawn_options.env = process.env;
326+
// let result = {
327+
// command: 'ruby',
328+
// args: [serverExe],
329+
// options: spawn_options,
330+
// }
331+
// let pdkDir: string = null;
332+
333+
// // type Platform = 'aix'
334+
// // | 'android'
335+
// // | 'darwin'
336+
// // | 'freebsd'
337+
// // | 'linux'
338+
// // | 'openbsd'
339+
// // | 'sunos'
340+
// // | 'win32';
341+
// switch (process.platform) {
342+
// case 'win32':
343+
// let comspec: string = process.env["COMSPEC"];
344+
// let programFiles = process.env["ProgramFiles"];
345+
// if (process.env["PROCESSOR_ARCHITEW6432"] == "AMD64") {
346+
// // VSCode is running as 32bit process on a 64bit Operating System. Need to break out
347+
// // of the 32bit using the sysnative redirection and environment variables
348+
// comspec = path.join(process.env["WINDIR"],"sysnative","cmd.exe");
349+
// programFiles = process.env["ProgramW6432"];
350+
// }
351+
352+
// pdkDir = path.join(programFiles, "Puppet Labs", "DevelopmentKit");
353+
354+
// result.options.stdio = 'pipe';
355+
// break;
356+
// default:
357+
// pdkDir = '/opt/puppetlabs/pdk';
358+
359+
// result.options.stdio = 'pipe';
360+
// result.options.shell = true;
361+
// break;
362+
// }
363+
// // Check if this really is a PDK installation
364+
// if (!fs.existsSync(path.join(pdkDir, "PDK_VERSION"))) {
365+
// this.logger.debug(logPrefix + "Could not find a valid PDK installation at " + pdkDir);
366+
// return null;
367+
// } else {
368+
// this.logger.debug(logPrefix + "Found a valid PDK installation at " + pdkDir);
369+
// }
370+
371+
// // Now to detect ruby versions
372+
// let subdirs = this.getDirectories(path.join(pdkDir,"private", "ruby"));
373+
// if (subdirs.length == 0) { return null; }
374+
// let rubyDir = path.join(pdkDir,"private", "ruby",subdirs[0]);
375+
376+
// subdirs = this.getDirectories(path.join(pdkDir,"share","cache","ruby"));
377+
// if (subdirs.length == 0) { return null; }
378+
// let gemDir = path.join(pdkDir,"share","cache","ruby",subdirs[0]);
379+
380+
// let rubylib = path.join(pdkDir,'lib')
381+
// if (process.platform == 'win32') {
382+
// // Translate all slashes to / style to avoid puppet/ruby issue #11930
383+
// rubylib = rubylib.replace(/\\/g,"/");
384+
// gemDir = gemDir.replace(/\\/g,"/");
385+
// }
386+
387+
// // Setup the process environment variables
388+
// if (result.options.env.PATH == undefined) { result.options.env.PATH = '' }
389+
// if (result.options.env.RUBYLIB == undefined) { result.options.env.RUBYLIB = '' }
390+
391+
// result.options.env.RUBY_DIR = rubyDir;
392+
// result.options.env.PATH = path.join(pdkDir,'bin') + this.pathEnvSeparator() + path.join(rubyDir,'bin') + this.pathEnvSeparator() + result.options.env.PATH;
393+
// result.options.env.RUBYLIB = path.join(pdkDir,'lib') + this.pathEnvSeparator() + result.options.env.RUBYLIB;
394+
// result.options.env.GEM_PATH = gemDir;
395+
// result.options.env.GEM_HOME = gemDir;
396+
// result.options.env.RUBYOPT = 'rubygems';
397+
398+
// this.logger.debug(logPrefix + "Using environment variable RUBY_DIR=" + result.options.env.RUBY_DIR);
399+
// this.logger.debug(logPrefix + "Using environment variable PATH=" + result.options.env.PATH);
400+
// this.logger.debug(logPrefix + "Using environment variable RUBYLIB=" + result.options.env.RUBYLIB);
401+
// this.logger.debug(logPrefix + "Using environment variable GEM_PATH=" + result.options.env.GEM_PATH);
402+
// this.logger.debug(logPrefix + "Using environment variable GEM_HOME=" + result.options.env.GEM_HOME);
403+
// this.logger.debug(logPrefix + "Using environment variable RUBYOPT=" + result.options.env.RUBYOPT);
404+
405+
// return result;
406+
// }
407+
408+
private createLanguageServerProcess(serverExe: string, callback : Function) {
409+
let logPrefix: string = '[createLanguageServerProcess] ';
410+
this.logger.debug(logPrefix + 'Language server found at: ' + serverExe)
411+
412+
let localServer = null
413+
414+
if (localServer == null) { localServer = this.getLanguageServerFromPuppetAgent(serverExe); }
415+
// if (localServer == null) { localServer = this.getLanguageServerFromPDK(serverExe); }
416+
417+
if (localServer == null) {
418+
this.logger.warning(logPrefix + "Could not find a valid Puppet Agent installation");
419+
this.setSessionFailure("Could not find a valid Puppet Agent installation");
420+
vscode.window.showWarningMessage('Could not find a valid Puppet Agent installation. Functionality will be limited to syntax highlighting');
281421
return;
282422
}
283423

284424
let connMgr : ConnectionManager = this;
285425
let logger = this.logger;
286426
// Start a server to get a random port
287-
this.logger.debug('Creating server process to identify random port')
427+
this.logger.debug(logPrefix + 'Creating server process to identify random port')
288428
const server = net.createServer()
289429
.on('close', () => {
290-
logger.debug('Server process to identify random port disconnected');
291-
connMgr.startLanguageServerProcess(cmd, args, options, callback);
430+
logger.debug(logPrefix + 'Server process to identify random port disconnected');
431+
connMgr.startLanguageServerProcess(localServer.command, localServer.args, localServer.options, callback);
292432
})
293433
.on('error', (err) => {
294434
throw err;
295435
});
296436

297437
// Listen on random port
298438
server.listen(0);
299-
this.logger.debug('Selected port for local language server: ' + server.address().port);
439+
this.logger.debug(logPrefix + 'Selected port for local language server: ' + server.address().port);
300440
connMgr.connectionConfiguration.port = server.address().port;
301441
server.close();
302442
}

0 commit comments

Comments
 (0)