Skip to content

Expand the concept of the homeDir on Windows #748

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

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
62 changes: 49 additions & 13 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,34 +516,70 @@ export function bufferFromFileOrString(file?: string, data?: string): Buffer | n
return null;
}

function dropDuplicatesAndNils(a: string[]): string[] {
return a.reduce(
(acceptedValues, currentValue) => {
// Good-enough algorithm for reducing a small (3 items at this point) array into an ordered list
// of unique non-empty strings.
if (currentValue && !acceptedValues.includes(currentValue)) {
return acceptedValues.concat(currentValue);
} else {
return acceptedValues;
}
},
[] as string[],
);
}

// Only public for testing.
export function findHomeDir(): string | null {
if (process.env.HOME) {
if (process.platform !== 'win32') {
if (process.env.HOME) {
try {
fs.accessSync(process.env.HOME);
return process.env.HOME;
// tslint:disable-next-line:no-empty
} catch (ignore) {}
}
return null;
}
// $HOME is always favoured, but the k8s go-client prefers the other two env vars
// differently depending on whether .kube/config exists or not.
const homeDrivePath =
process.env.HOMEDRIVE && process.env.HOMEPATH
? path.join(process.env.HOMEDRIVE, process.env.HOMEPATH)
: '';
const homePath = process.env.HOME || '';
const userProfile = process.env.USERPROFILE || '';
const favourHomeDrivePathList: string[] = dropDuplicatesAndNils([homePath, homeDrivePath, userProfile]);
const favourUserProfileList: string[] = dropDuplicatesAndNils([homePath, userProfile, homeDrivePath]);
// 1. the first of %HOME%, %HOMEDRIVE%%HOMEPATH%, %USERPROFILE% containing a `.kube\config` file is returned.
for (const dir of favourHomeDrivePathList) {
try {
fs.accessSync(process.env.HOME);
return process.env.HOME;
fs.accessSync(path.join(dir, '.kube', 'config'));
return dir;
// tslint:disable-next-line:no-empty
} catch (ignore) {}
}
if (process.platform !== 'win32') {
return null;
}
if (process.env.HOMEDRIVE && process.env.HOMEPATH) {
const dir = path.join(process.env.HOMEDRIVE, process.env.HOMEPATH);
// 2. ...the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that exists and is writeable is returned
for (const dir of favourUserProfileList) {
try {
fs.accessSync(dir);
fs.accessSync(dir, fs.constants.W_OK);
return dir;
// tslint:disable-next-line:no-empty
} catch (ignore) {}
}
if (process.env.USERPROFILE) {
// 3. ...the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that exists is returned.
for (const dir of favourUserProfileList) {
try {
fs.accessSync(process.env.USERPROFILE);
return process.env.USERPROFILE;
fs.accessSync(dir);
return dir;
// tslint:disable-next-line:no-empty
} catch (ignore) {}
}
return null;
// 4. if none of those locations exists, the first of
// %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that is set is returned.
return favourUserProfileList[0] || null;
}

export interface Named {
Expand Down
139 changes: 117 additions & 22 deletions src/config_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,39 +461,134 @@ describe('KubeConfig', () => {
process.env.HOMEPATH = originalEnvVars.HOMEPATH;
});

it('should return null if no home is present', () => {
it('should return null if no home-ish env vars are set', () => {
const dir = findHomeDir();
expect(dir).to.equal(null);
});

it('should load from HOMEDRIVE/HOMEPATH if present', () => {
process.env.HOMEDRIVE = 'foo';
process.env.HOMEPATH = 'bar';
const dir = join(process.env.HOMEDRIVE, process.env.HOMEPATH);
const arg = {};
arg[dir] = { config: 'data' };
mockfs(arg);
describe('look for an existing .kube/config', () => {
let allDirs;
let homeDrive;
beforeEach(() => {
allDirs = {};
process.env.HOME = 'home';
process.env.HOMEDRIVE = 'drive';
process.env.HOMEPATH = 'a-path';
process.env.USERPROFILE = 'a-userprofile';
homeDrive = join(process.env.HOMEDRIVE, process.env.HOMEPATH);
allDirs[process.env.HOME] = {};
allDirs[homeDrive] = {};
allDirs[process.env.USERPROFILE] = {};
});
it('should load from HOME if present', () => {
const dir = process.env.HOME as string;
allDirs[dir]['.kube'] = { config: 'data' };
mockfs(allDirs);

const home = findHomeDir();
const home = findHomeDir();

mockfs.restore();
expect(home).to.equal(dir);
mockfs.restore();
expect(home).to.equal(dir);
});
it('should favor HOME when present', () => {
const dir = process.env.HOME as string;
allDirs[dir]['.kube'] = { config: 'data' };
allDirs[homeDrive]['.kube'] = { config: 'data' };
allDirs[process.env.USERPROFILE as string]['.kube'] = { config: 'data' };
mockfs(allDirs);

const home = findHomeDir();

mockfs.restore();
expect(home).to.equal(dir);
});

it('should load from HOMEDRIVE/HOMEPATH if present', () => {
const dir = homeDrive;
allDirs[dir]['.kube'] = { config: 'data' };
mockfs(allDirs);

const home = findHomeDir();

mockfs.restore();
expect(home).to.equal(dir);
});

it('should favor HOMEDRIVE/HOMEPATH over USERPROFILE', () => {
const dir = homeDrive;
allDirs[dir]['.kube'] = { config: 'data' };
allDirs[process.env.USERPROFILE as string]['.kube'] = { config: 'data' };
mockfs(allDirs);

const home = findHomeDir();

mockfs.restore();
expect(home).to.equal(dir);
});

it('should load from USERPROFILE if present', () => {
const dir = process.env.USERPROFILE as string;
allDirs[dir]['.kube'] = { config: 'data' };
mockfs(allDirs);

const home = findHomeDir();

mockfs.restore();
expect(home).to.equal(dir);
});
});

it('should load from USERPROFILE if present', () => {
const dir = 'someplace';
// Just test for existence,but this will include the writeability order
describe('look for an existing directory', () => {
let allDirs;
let homeDrive;
beforeEach(() => {
allDirs = {};
process.env.HOME = 'home';
process.env.HOMEDRIVE = 'drive';
process.env.HOMEPATH = 'a-path';
process.env.USERPROFILE = 'a-userprofile';
homeDrive = join(process.env.HOMEDRIVE, process.env.HOMEPATH);
});
it('should load from HOME if present', () => {
allDirs[process.env.HOME as string] = 'data';
allDirs[homeDrive] = 'data';
allDirs[process.env.USERPROFILE as string] = 'data';
const dir = process.env.HOME;
mockfs(allDirs);

process.env.HOMEDRIVE = 'foo';
process.env.HOMEPATH = 'bar';
process.env.USERPROFILE = dir;
const arg = {};
arg[dir] = { config: 'data' };
mockfs(arg);
const home = findHomeDir();

const home = findHomeDir();
mockfs.restore();
expect(home).to.equal(dir);
});
it('should load from USERPROFILE if present', () => {
allDirs[homeDrive] = 'data';
allDirs[process.env.USERPROFILE as string] = 'data';
mockfs(allDirs);

mockfs.restore();
expect(home).to.equal(dir);
const home = findHomeDir();

mockfs.restore();
expect(home).to.equal(process.env.USERPROFILE);
});
it('should load from homeDrive if present', () => {
allDirs[homeDrive] = 'data';
mockfs(allDirs);

const home = findHomeDir();

mockfs.restore();
expect(home).to.equal(homeDrive);
});
it('should return HOME when no home-ish directories are present', () => {
mockfs({});

const home = findHomeDir();

mockfs.restore();
expect(home).to.equal(process.env.HOME);
});
});
});

Expand Down