Validating urls in service creation (#269)

* validating urls in service creation

* Supporting SSH urls
This commit is contained in:
Bhargav Nookala 2020-02-11 11:08:16 -08:00 коммит произвёл GitHub
Родитель c5368113d6
Коммит fbd2b671a8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 135 добавлений и 6 удалений

Просмотреть файл

@ -82,6 +82,7 @@
"shelljs": "^0.8.3",
"simple-git": "^1.126.0",
"spektate": "^0.1.18",
"ssh-url": "^0.1.5",
"uuid": "^3.3.3",
"winston": "^3.2.1"
},

Просмотреть файл

@ -19,7 +19,13 @@ import {
createTestBedrockYaml,
createTestMaintainersYaml
} from "../../test/mockFactory";
import { createService, execute, fetchValues, ICommandValues } from "./create";
import {
createService,
execute,
fetchValues,
ICommandValues,
validateGitUrl
} from "./create";
jest.mock("../../lib/gitutils");
beforeAll(() => {
@ -93,14 +99,16 @@ describe("Test fetchValues function", () => {
expect(err).toBeDefined();
}
});
it("Postive test: with middlewares value", () => {
it("Positive test: with middlewares value", () => {
jest.spyOn(config, "Bedrock").mockReturnValueOnce(BedrockMockedContent);
const mocked = getMockValues();
mocked.middlewares = "mid1, mid2"; // space after comma is intentional, expecting trimming to happen
const result = fetchValues(mocked);
expect(result.middlewaresArray).toEqual(["mid1", "mid2"]);
});
it("Postive test: with bedrock rings", () => {
it("Positive test: with bedrock rings", () => {
const mockedBedrockFileConfig = { ...BedrockMockedContent };
mockedBedrockFileConfig.rings = {
master: {},
@ -112,13 +120,15 @@ describe("Test fetchValues function", () => {
const result = fetchValues(mocked);
expect(result.ringNames).toEqual(["master", "qa"]);
});
it("Postive test", () => {
it("Positive test", () => {
const mocked = getMockValues();
jest.spyOn(config, "Bedrock").mockReturnValueOnce(BedrockMockedContent);
const result = fetchValues(mocked);
expect(result).toEqual(mocked);
});
});
describe("Test execute function", () => {
it("Negative test: without service name", async () => {
const exitFn = jest.fn();
@ -126,19 +136,23 @@ describe("Test execute function", () => {
expect(exitFn).toBeCalledTimes(1);
expect(exitFn.mock.calls).toEqual([[1]]);
});
it("Negative test: service name is cwd and missing display name", async () => {
const exitFn = jest.fn();
await execute(".", getMockValues(), exitFn);
expect(exitFn).toBeCalledTimes(1);
expect(exitFn.mock.calls).toEqual([[1]]);
});
it("Negative test: missing bedrock file", async () => {
const testServiceName = uuid();
const exitFn = jest.fn();
jest.spyOn(bedrockYaml, "fileInfo").mockImplementation(() => ({
exist: false,
hasVariableGroups: false
}));
try {
await execute(testServiceName, getMockValues(), exitFn);
expect(exitFn).toBeCalledTimes(1);
@ -147,13 +161,16 @@ describe("Test execute function", () => {
removeDir(testServiceName); // housekeeping
}
});
it("Negative test: missing bedrock variable groups", async () => {
const testServiceName = uuid();
const exitFn = jest.fn();
jest.spyOn(bedrockYaml, "fileInfo").mockImplementation(() => ({
exist: true,
hasVariableGroups: false
}));
try {
await execute(testServiceName, getMockValues(), exitFn);
expect(exitFn).toBeCalledTimes(1);
@ -164,6 +181,49 @@ describe("Test execute function", () => {
});
});
describe("Validate Git URLs", () => {
it("Should error when providing an invalid git url", async () => {
const exitFn = jest.fn();
const helmConfigPath = "dev.azure.com/foo/bar";
validateGitUrl(helmConfigPath, exitFn);
expect(exitFn).toBeCalledTimes(1);
});
it("Should not error when providing a valid github https url", async () => {
const exitFn = jest.fn();
const helmConfigPath = "https://github.com/CatalystCode/spk.git";
validateGitUrl(helmConfigPath, exitFn);
expect(exitFn).toBeCalledTimes(0);
});
it("Should not error when providing a valid azdo https url", async () => {
const exitFn = jest.fn();
const helmConfigPath =
"https://dev@dev.azure.com/catalystcode/project/_git/repo";
validateGitUrl(helmConfigPath, exitFn);
expect(exitFn).toBeCalledTimes(0);
});
it("Should not error when providing a valid azdo git+ssh url", async () => {
const exitFn = jest.fn();
const helmConfigPath = "git@ssh.dev.azure.com:v3/CatalystCode/project/repo";
validateGitUrl(helmConfigPath, exitFn);
expect(exitFn).toBeCalledTimes(0);
});
it("Should not error when providing a valid github git+ssh url", async () => {
const exitFn = jest.fn();
const helmConfigPath = "git@github.com:CatalystCode/spk.git";
validateGitUrl(helmConfigPath, exitFn);
expect(exitFn).toBeCalledTimes(0);
});
});
describe("Adding a service to a repo directory", () => {
let randomTmpDir: string = "";
beforeEach(async () => {
@ -171,7 +231,7 @@ describe("Adding a service to a repo directory", () => {
randomTmpDir = createTempDir();
});
test("New service is created in projet root directory. No display name given, so this should throw an error.", async () => {
test("New service is created in project root directory. No display name given, so this should throw an error.", async () => {
await writeSampleMaintainersFileToDir(
path.join(randomTmpDir, "maintainers.yaml")
);
@ -198,7 +258,7 @@ describe("Adding a service to a repo directory", () => {
expect(hasError).toBe(true);
});
test("New service is created in projet root directory. With display name given, so this work fine.", async () => {
test("New service is created in project root directory. With display name given, so this works fine.", async () => {
await writeSampleMaintainersFileToDir(
path.join(randomTmpDir, "maintainers.yaml")
);

Просмотреть файл

@ -1,6 +1,8 @@
import { fail } from "assert";
import commander from "commander";
import path from "path";
import shelljs from "shelljs";
import * as sshUrl from "ssh-url";
import { Bedrock } from "../../config";
import {
addNewService as addNewServiceToBedrockFile,
@ -109,6 +111,7 @@ export const execute = async (
await exitFn(1);
return;
}
if (serviceName === "." && opts.displayName === "") {
logger.error(
`If specifying the current directory as service name, please incluce a display name using '-n'`
@ -116,6 +119,10 @@ export const execute = async (
await exitFn(1);
return;
}
// Sanity checking the specified Helm URLs
await validateGitUrl(opts.helmConfigGit, exitFn);
const projectPath = process.cwd();
logger.verbose(`project path: ${projectPath}`);
@ -133,6 +140,51 @@ export const execute = async (
}
};
/**
* Validates a helm config git URI, if one is provided through the CLI
* Silently returns if nothing is wrong with it, otherwise errors loudly.
* @param gitUrl A URL to a helm chart
* @param exitFn A function to call to exit the process.
*/
export const validateGitUrl = async (
gitUrl: string,
exitFn: (status: number) => void
): Promise<void> => {
if (gitUrl === "") {
return;
}
let isHelmConfigHttp = true;
try {
// tslint:disable-next-line: no-unused-expression
new URL(gitUrl);
} catch (err) {
logger.warn(
`Provided helm git URL is an invalid http/https URL: ${gitUrl}`
);
isHelmConfigHttp = false;
}
// We might be looking at a git+ssh URL ie: git@foo.com:/path/to/git
if (!isHelmConfigHttp) {
try {
const parsedSshUrl = sshUrl.parse(gitUrl);
// Git url parsed by node-ssh-url will have a `user` field if it resembles
// git@ssh.dev.azure.com:v3/bhnook/test/hld
if (parsedSshUrl.user === null) {
fail("Not a valid git+ssh url");
}
} catch (err) {
logger.error(
`Provided helm git URL is an invalid git+ssh or http/https URL: ${gitUrl}`
);
await exitFn(1);
return;
}
}
};
/**
* Adds the create command to the service command object
*

11
src/commands/service/ssh-url.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
declare module "ssh-url" {
export interface ISshUrlObj {
protocol: null | string;
user: string;
hostname: string;
pathname: string;
}
export function parse(gitSshUrl: string): ISshUrlObj;
export function format(sshUrlObj: ISshUrlObj): string;
}

Просмотреть файл

@ -6212,6 +6212,11 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
ssh-url@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/ssh-url/-/ssh-url-0.1.5.tgz#e931955c6492277fac3f8824f71f02880a38fd72"
integrity sha1-6TGVXGSSJ3+sP4gk9x8CiAo4/XI=
sshpk@^1.7.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"