Validating urls in service creation (#269)
* validating urls in service creation * Supporting SSH urls
This commit is contained in:
Родитель
c5368113d6
Коммит
fbd2b671a8
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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"
|
||||
|
|
Загрузка…
Ссылка в новой задаче