Skip to content
This repository was archived by the owner on Apr 13, 2020. It is now read-only.

Commit cb369fc

Browse files
authored
spk ring create (#365)
* spk ring craete * unit tests * minor cleanup * feedback
1 parent eb855b8 commit cb369fc

File tree

12 files changed

+320
-33
lines changed

12 files changed

+320
-33
lines changed

src/commands/ring/create.test.ts

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { create as createBedrockYaml } from "../../lib/bedrockYaml";
2+
import { read as loadBedrockFile } from "../../lib/bedrockYaml";
23
import { createTempDir } from "../../lib/ioUtil";
34
import { disableVerboseLogging, enableVerboseLogging } from "../../logger";
45

6+
import * as fileUtils from "../../lib/fileutils";
7+
8+
import { IBedrockFile } from "../../types";
59
import { checkDependencies, execute } from "./create";
610

711
beforeAll(() => {
@@ -12,19 +16,50 @@ afterAll(() => {
1216
disableVerboseLogging();
1317
});
1418

15-
describe("test valid function", () => {
16-
it("negative test", async () => {
17-
try {
18-
const tmpDir = createBedrockYaml();
19-
checkDependencies(tmpDir);
20-
expect(true).toBe(false);
21-
} catch (e) {
22-
expect(e).not.toBeNull();
23-
}
19+
describe("checkDependencies", () => {
20+
it("Project not initialized, it should fail.", async () => {
21+
const tmpDir = createTempDir();
22+
expect(() => {
23+
checkDependencies(tmpDir, "");
24+
}).toThrow();
25+
});
26+
it("Project initialized, it should pass.", async () => {
27+
const tmpDir = createBedrockYaml();
28+
createBedrockYaml(tmpDir, {
29+
rings: {
30+
master: {
31+
isDefault: true
32+
}
33+
},
34+
services: {},
35+
variableGroups: ["testvg"]
36+
});
37+
checkDependencies(tmpDir, "not-master");
38+
// No errors thrown, this is a pass for the function.
39+
});
40+
it("Project initialized, but ring already exists, it should fail.", async () => {
41+
const tmpDir = createBedrockYaml(undefined, {
42+
rings: {
43+
master: {
44+
isDefault: true
45+
}
46+
},
47+
services: {},
48+
variableGroups: ["testvg"]
49+
});
50+
expect(() => {
51+
checkDependencies(tmpDir, "master");
52+
}).toThrow();
2453
});
2554
});
2655

2756
describe("test execute function and logic", () => {
57+
it("test execute function: missing ring input", async () => {
58+
const exitFn = jest.fn();
59+
await execute("", "someprojectpath", exitFn);
60+
expect(exitFn).toBeCalledTimes(1);
61+
expect(exitFn.mock.calls).toEqual([[1]]);
62+
});
2863
it("test execute function: missing project path", async () => {
2964
const exitFn = jest.fn();
3065
await execute("ring", "", exitFn);
@@ -33,19 +68,47 @@ describe("test execute function and logic", () => {
3368
});
3469
it("test execute function: working path with bedrock.yaml", async () => {
3570
const exitFn = jest.fn();
36-
71+
const mockPipelineUpdate = jest.spyOn(
72+
fileUtils,
73+
"updateTriggerBranchesForServiceBuildAndUpdatePipeline"
74+
);
75+
mockPipelineUpdate.mockImplementation();
3776
const tmpDir = createTempDir();
77+
3878
createBedrockYaml(tmpDir, {
3979
rings: {
4080
master: {
4181
isDefault: true
4282
}
4383
},
44-
services: {},
84+
services: {
85+
"./my-service": {
86+
helm: {
87+
chart: {
88+
branch: "master",
89+
git: "https://github.com/catalystcode/spk-demo-repo.git",
90+
path: "my-service"
91+
}
92+
},
93+
k8sBackendPort: 80
94+
}
95+
},
4596
variableGroups: ["testvg"]
4697
});
47-
await execute("ring", tmpDir, exitFn);
4898

99+
const newRingName = "my-new-ring";
100+
const oldBedrockFile: IBedrockFile = loadBedrockFile(tmpDir);
101+
expect(
102+
Object.entries(oldBedrockFile.rings).map(([ring]) => ring)
103+
).not.toContain(newRingName);
104+
105+
await execute(newRingName, tmpDir, exitFn);
106+
107+
const updatedBedrockFile: IBedrockFile = loadBedrockFile(tmpDir);
108+
expect(
109+
Object.entries(updatedBedrockFile.rings).map(([ring]) => ring)
110+
).toContain(newRingName);
111+
expect(mockPipelineUpdate).toBeCalledTimes(1);
49112
expect(exitFn).toBeCalledTimes(1);
50113
expect(exitFn.mock.calls).toEqual([[0]]);
51114
});

src/commands/ring/create.ts

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import commander from "commander";
2-
import { fileInfo as bedrockFileInfo } from "../../lib/bedrockYaml";
2+
import {
3+
addNewRing,
4+
fileInfo as bedrockFileInfo,
5+
read as loadBedrockFile
6+
} from "../../lib/bedrockYaml";
37
import { build as buildCmd, exit as exitCmd } from "../../lib/commandBuilder";
4-
import { PROJECT_INIT_DEPENDENCY_ERROR_MESSAGE } from "../../lib/constants";
8+
import {
9+
BEDROCK_FILENAME,
10+
PROJECT_INIT_DEPENDENCY_ERROR_MESSAGE
11+
} from "../../lib/constants";
12+
import { updateTriggerBranchesForServiceBuildAndUpdatePipeline } from "../../lib/fileutils";
513
import { hasValue } from "../../lib/validator";
614
import { logger } from "../../logger";
7-
import { IBedrockFileInfo } from "../../types";
8-
15+
import { IBedrockFile, IBedrockFileInfo } from "../../types";
916
import decorator from "./create.decorator.json";
1017

1118
/**
@@ -20,18 +27,31 @@ export const execute = async (
2027
exitFn: (status: number) => Promise<void>
2128
) => {
2229
if (!hasValue(ringName)) {
30+
logger.error(`No ring name given.`);
2331
await exitFn(1);
2432
return;
2533
}
2634

2735
try {
28-
logger.info(`project path: ${projectPath}`);
36+
logger.info(`Project path: ${projectPath}`);
2937

30-
checkDependencies(projectPath);
38+
checkDependencies(projectPath, ringName);
3139

32-
// Check if ring already exists, if it does, warn and exit
3340
// Add ring to bedrock.yaml
41+
addNewRing(projectPath, ringName);
3442
// Add ring to all linked service build pipelines' branch triggers
43+
const bedrockFile: IBedrockFile = loadBedrockFile(projectPath);
44+
45+
const newRings = Object.entries(bedrockFile.rings).map(([ring]) => ring);
46+
logger.info(`Updated project rings: ${newRings}`);
47+
48+
const servicePathDirectories = Object.entries(bedrockFile.services).map(
49+
([serviceRelativeDir]) => serviceRelativeDir
50+
);
51+
52+
servicePathDirectories.forEach(s => {
53+
updateTriggerBranchesForServiceBuildAndUpdatePipeline(newRings, s);
54+
});
3555

3656
logger.info(`Successfully created ring: ${ringName} for this project!`);
3757
await exitFn(0);
@@ -54,9 +74,17 @@ export const commandDecorator = (command: commander.Command): void => {
5474
* Check for bedrock.yaml
5575
* @param projectPath
5676
*/
57-
export const checkDependencies = (projectPath: string) => {
77+
export const checkDependencies = (projectPath: string, ringName: string) => {
5878
const fileInfo: IBedrockFileInfo = bedrockFileInfo(projectPath);
5979
if (fileInfo.exist === false) {
6080
throw new Error(PROJECT_INIT_DEPENDENCY_ERROR_MESSAGE);
6181
}
82+
83+
// Check if ring already exists, if it does, warn and exit
84+
const bedrockFile: IBedrockFile = loadBedrockFile(projectPath);
85+
if (ringName in bedrockFile.rings) {
86+
throw new Error(
87+
`ring: ${ringName} already exists in project ${BEDROCK_FILENAME}.`
88+
);
89+
}
6290
};

src/commands/ring/delete.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const execute = async (
2525
}
2626

2727
try {
28-
logger.info(`project path: ${projectPath}`);
28+
logger.info(`Project path: ${projectPath}`);
2929

3030
checkDependencies(projectPath);
3131

src/commands/ring/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Command } from "../command";
22

3-
const subcommands = ["add", "delete", "set-default"];
3+
const subcommands = ["create", "delete", "set-default"];
44

55
export const commandDecorator = Command(
66
"ring",

src/commands/ring/set-default.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const execute = async (
2525
}
2626

2727
try {
28-
logger.info(`project path: ${projectPath}`);
28+
logger.info(`Project path: ${projectPath}`);
2929

3030
checkDependencies(projectPath);
3131

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const commandModules = [
2424
"hld",
2525
"infra",
2626
"project",
27-
// "ring", Uncomment when ready to add rings
27+
// "ring", // Uncomment when ready to add rings
2828
"service",
2929
"variable-group"
3030
];

src/lib/bedrockYaml.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createTempDir } from "../lib/ioUtil";
33
import { createTestBedrockYaml } from "../test/mockFactory";
44
import { IBedrockFile, IHelmConfig } from "../types";
55
import {
6+
addNewRing,
67
addNewService,
78
create,
89
DEFAULT_CONTENT,
@@ -74,7 +75,11 @@ describe("Adding a new service to a Bedrock file", () => {
7475
);
7576

7677
const expected: IBedrockFile = {
77-
rings: {},
78+
rings: {
79+
master: {
80+
isDefault: true
81+
}
82+
},
7883
services: {
7984
...(defaultBedrockFileObject as IBedrockFile).services,
8085
["./" + servicePath]: {
@@ -94,6 +99,34 @@ describe("Adding a new service to a Bedrock file", () => {
9499
});
95100
});
96101

102+
describe("Adding a new ring to an existing bedrock.yaml", () => {
103+
it("should update existing bedrock.yaml with a new service and its helm chart config", () => {
104+
const defaultBedrockFileObject = createTestBedrockYaml(
105+
false
106+
) as IBedrockFile;
107+
108+
// "" means that bedrock.yaml is written to a random directory
109+
const dir = create("", defaultBedrockFileObject);
110+
111+
const ringName = "new-ring";
112+
113+
addNewRing(dir, ringName);
114+
115+
const expected: IBedrockFile = {
116+
rings: {
117+
...(defaultBedrockFileObject as IBedrockFile).rings,
118+
[ringName]: {}
119+
},
120+
services: {
121+
...(defaultBedrockFileObject as IBedrockFile).services
122+
},
123+
variableGroups: []
124+
};
125+
126+
expect(read(dir)).toEqual(expected);
127+
});
128+
});
129+
97130
describe("Bedrock file info", () => {
98131
it("Should File exist and hasVariableGroups both be false", () => {
99132
const dir = createTempDir();

src/lib/bedrockYaml.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export const read = (dir: string): IBedrockFile => {
6363
};
6464

6565
/**
66-
* Update bedrock.yml with new service
66+
* Update bedrock.yaml with new service
6767
*
6868
* @param dir Directory where <code>bedrock.yaml</code> file resides.
6969
* @param newServicePath Service Path
@@ -105,6 +105,24 @@ export const addNewService = (
105105
fs.writeFileSync(path.join(absPath, YAML_NAME), asYaml);
106106
};
107107

108+
/**
109+
* Update bedrock.yaml with new ring
110+
*
111+
* @param dir Directory where <code>bedrock.yaml</code> file resides.
112+
* @param ringName ring to be added.
113+
*/
114+
export const addNewRing = (dir: string, ringName: string) => {
115+
const absPath = path.resolve(dir);
116+
const data: IBedrockFile = read(absPath);
117+
118+
data.rings[ringName] = {}; // Alternatively, we can set isDefault = false or some passable value.
119+
120+
const asYaml = yaml.safeDump(data, {
121+
lineWidth: Number.MAX_SAFE_INTEGER
122+
});
123+
fs.writeFileSync(path.join(absPath, YAML_NAME), asYaml);
124+
};
125+
108126
/**
109127
* Returns bedrock file information
110128
*

src/lib/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export const ACCESS_FILENAME = "access.yaml";
22

3+
export const BEDROCK_FILENAME = "bedrock.yaml";
4+
35
export const BUILD_SCRIPT_URL =
46
"https://raw.githubusercontent.com/Microsoft/bedrock/master/gitops/azure-devops/build.sh";
57

0 commit comments

Comments
 (0)