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

Commit 24f37f2

Browse files
authored
Enforces running SPK Project commands in order and Lifecycle pipeline refactoring to use variable groups from Bedrock.yaml (#199)
* refactored cvg and test cases * Added validation check in pipeline install command * Removed variable in build definiiton. Added variable group names in lifecycle piepline from bedrock.yaml. Fixed create variable group bug * Removed unnecessary check for bedrock file content * Incorporated feedback * Reverted changes related to variable group bug. * Added test cases
1 parent e02f4c0 commit 24f37f2

File tree

12 files changed

+371
-142
lines changed

12 files changed

+371
-142
lines changed

docs/project-management.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Project Management
22

3-
Create and manage components for a Bedrock project.
3+
Create and manage components for a Bedrock project. All project management
4+
commands will need to run in the order as listed below in the
5+
[commands](#Commands) section due to dependencies.
46

57
Usage:
68

@@ -11,13 +13,18 @@ spk project [command] [options]
1113
Commands:
1214

1315
- [Project Management](#project-management)
16+
1417
- [Prerequisites](#prerequisites)
1518
- [Commands](#commands)
19+
1620
- [init](#init)
1721
- [create-variable-group](#create-variable-group)
1822
- [Command Prerequisites](#command-prerequisites)
1923
- [install-lifecycle-pipeline](#install-lifecycle-pipeline)
2024

25+
**Please note all project management commands must run in the order as
26+
listed above.**
27+
2128
Global options:
2229

2330
```

docs/service-introspection-onboarding.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ by service introspection.
148148
- Add the task before the crucial steps in your pipeline. This will capture
149149
details about failures if the important steps fail.
150150

151-
- To specify the `spk` version you want to download, set the `VERSION` environment variable. With this, `get_spk_version` will download that version. If `VERSION` is not set, it will download the latest version.
151+
- To specify the `spk` version you want to download, set the `VERSION`
152+
environment variable. With this, `get_spk_version` will download that version.
153+
If `VERSION` is not set, it will download the latest version.
152154

153155
#### 3. CD release pipeline (ACR to HLD) configuration
154156

@@ -167,14 +169,13 @@ pipeline in the Azure DevOps portal:
167169

168170
```yaml
169171
latest_commit=$(git rev-parse --short HEAD) VERSION_TO_DOWNLOAD=$(curl -s
170-
"https://api.github.com/repos/CatalystCode/spk/releases/latest" | grep "tag_name" | sed -E 's/.*"([^"]+)".*/\1/')
171-
echo "Downloading SPK"
172-
curl https://raw.githubusercontent.com/Microsoft/bedrock/master/gitops/azure-devops/build.sh > build.sh
173-
chmod +x build.sh
174-
. ./build.sh --source-only
175-
get_spk_version
176-
download_spk
177-
./spk/spk deployment create -n $(ACCOUNT_NAME) -k $(ACCOUNT_KEY) -t $(TABLE_NAME) -p $(PARTITION_KEY) --p2 $(Release.ReleaseId) --hld-commit-id $latest_commit --env $(Release.EnvironmentName) --image-tag $(Build.BuildId)
172+
"https://api.github.com/repos/CatalystCode/spk/releases/latest" | grep
173+
"tag_name" | sed -E 's/.*"([^"]+)".*/\1/') echo "Downloading SPK" curl
174+
https://raw.githubusercontent.com/Microsoft/bedrock/master/gitops/azure-devops/build.sh
175+
> build.sh chmod +x build.sh . ./build.sh --source-only get_spk_version
176+
download_spk ./spk/spk deployment create -n $(ACCOUNT_NAME) -k $(ACCOUNT_KEY)
177+
-t $(TABLE_NAME) -p $(PARTITION_KEY) --p2 $(Release.ReleaseId) --hld-commit-id
178+
$latest_commit --env $(Release.EnvironmentName) --image-tag $(Build.BuildId)
178179
```
179180

180181
This task is similar to the one from step 1 but instead passes the information
@@ -191,14 +192,13 @@ your multi-stage `azure-pipelines.yml`:
191192
```yaml
192193
latest_commit=$(git rev-parse --short HEAD) tag_name=$(Build.BuildId)
193194
VERSION_TO_DOWNLOAD=$(curl -s
194-
"https://api.github.com/repos/CatalystCode/spk/releases/latest" | grep "tag_name" | sed -E 's/.*"([^"]+)".*/\1/')
195-
echo "Downloading SPK"
196-
curl https://raw.githubusercontent.com/Microsoft/bedrock/master/gitops/azure-devops/build.sh > build.sh
197-
chmod +x build.sh
198-
. ./build.sh --source-only
199-
get_spk_version
200-
download_spk
201-
./spk/spk deployment create -n $(ACCOUNT_NAME) -k $(ACCOUNT_KEY) -t $(TABLE_NAME) -p $(PARTITION_KEY) --p2 $(Build.BuildId) --hld-commit-id $latest_commit --env $(Build.SourceBranchName) --image-tag $tag_name
195+
"https://api.github.com/repos/CatalystCode/spk/releases/latest" | grep
196+
"tag_name" | sed -E 's/.*"([^"]+)".*/\1/') echo "Downloading SPK" curl
197+
https://raw.githubusercontent.com/Microsoft/bedrock/master/gitops/azure-devops/build.sh
198+
> build.sh chmod +x build.sh . ./build.sh --source-only get_spk_version
199+
download_spk ./spk/spk deployment create -n $(ACCOUNT_NAME) -k $(ACCOUNT_KEY)
200+
-t $(TABLE_NAME) -p $(PARTITION_KEY) --p2 $(Build.BuildId) --hld-commit-id
201+
$latest_commit --env $(Build.SourceBranchName) --image-tag $tag_name
202202
```
203203

204204
Make sure your variable `tag_name` in this script matches the `tag_name` in the

src/commands/project/create-variable-group.test.ts

Lines changed: 196 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
import fs from "fs";
2+
import yaml from "js-yaml";
3+
import mockFs from "mock-fs";
24
import os from "os";
35
import path from "path";
46
import uuid from "uuid/v4";
5-
import { readYaml, write } from "../../config";
7+
import { Bedrock, readYaml, write } from "../../config";
68
import { IAzureDevOpsOpts } from "../../lib/git";
79
import {
810
disableVerboseLogging,
911
enableVerboseLogging,
1012
logger
1113
} from "../../logger";
12-
import { IBedrockFile } from "../../types";
14+
import {
15+
createTestBedrockYaml,
16+
createTestHldLifecyclePipelineYaml
17+
} from "../../test/mockFactory";
18+
import { IAzurePipelinesYaml, IBedrockFile } from "../../types";
1319
import {
1420
create,
1521
execute,
22+
isBedrockFileExists,
1623
setVariableGroupInBedrockFile,
24+
updateLifeCyclePipeline,
1725
validateRequiredArguments
1826
} from "./create-variable-group";
1927

@@ -25,6 +33,10 @@ afterAll(() => {
2533
disableVerboseLogging();
2634
});
2735

36+
beforeEach(() => {
37+
jest.clearAllMocks();
38+
});
39+
2840
const registryName = uuid();
2941
const variableGroupName = uuid();
3042
const hldRepoUrl = uuid();
@@ -255,58 +267,52 @@ describe("setVariableGroupInBedrockFile", () => {
255267
expect(invalidGroupNameError).toBeDefined();
256268
});
257269

258-
test("Should pass adding a variable group name when no bedrock file exists", async () => {
270+
test("Should fail adding a variable group name when no bedrock file exists", async () => {
259271
// Create random directory to initialize
260272
const randomTmpDir = path.join(os.tmpdir(), uuid());
261273
fs.mkdirSync(randomTmpDir);
262274

263-
await setVariableGroupInBedrockFile(randomTmpDir, variableGroupName);
264-
265-
const filePath = path.join(randomTmpDir, "bedrock.yaml");
266-
expect(fs.existsSync(filePath)).toBe(true);
267-
268-
const bedrockFileData = readYaml<IBedrockFile>(filePath);
269-
logger.info(`filejson: ${JSON.stringify(bedrockFileData)}`);
270-
expect(bedrockFileData.variableGroups![0]).toBe(variableGroupName);
271-
});
275+
let noFileError: Error | undefined;
272276

273-
test("Should pass adding a valid variable group name when bedrock file exists with undefined variableGroups", async () => {
274-
// Create random directory to initialize
275-
const randomTmpDir = path.join(os.tmpdir(), uuid());
276-
fs.mkdirSync(randomTmpDir);
277-
278-
await setVariableGroupInBedrockFile(randomTmpDir, variableGroupName);
279-
280-
const bedrockFilePath = path.join(randomTmpDir, "bedrock.yaml");
281-
expect(fs.existsSync(bedrockFilePath)).toBe(true);
282-
283-
const bedrockFile = readYaml<IBedrockFile>(bedrockFilePath);
284-
logger.info(`filejson: ${JSON.stringify(bedrockFile)}`);
285-
expect(bedrockFile.variableGroups![0]).toBe(variableGroupName);
277+
try {
278+
await setVariableGroupInBedrockFile(randomTmpDir, variableGroupName);
279+
} catch (err) {
280+
logger.info(`${err}`);
281+
noFileError = err;
282+
}
283+
expect(noFileError).toBeDefined();
286284
});
287285

288286
test("Should pass adding a valid variable group name when bedrock file exists with empty variableGroups", async () => {
289287
// Create random directory to initialize
290288
const randomTmpDir = path.join(os.tmpdir(), uuid());
291289
fs.mkdirSync(randomTmpDir);
292290

291+
// create bedrock file to simulate the the use case that `spk project init` ran before
293292
const bedrockFileData: IBedrockFile = {
294-
rings: {}, // rings is optional but necessary to create a bedrock file in config.write method
295-
services: {}, // service property is not optional so set it to null
293+
rings: {},
294+
services: {},
296295
variableGroups: []
297296
};
298297

298+
const asYaml = yaml.safeDump(bedrockFileData, {
299+
lineWidth: Number.MAX_SAFE_INTEGER
300+
});
301+
302+
fs.writeFileSync(path.join(randomTmpDir, "bedrock.yaml"), asYaml);
303+
299304
await setVariableGroupInBedrockFile(randomTmpDir, variableGroupName);
300305

301306
const bedrockFilePath = path.join(randomTmpDir, "bedrock.yaml");
302307
expect(fs.existsSync(bedrockFilePath)).toBe(true);
303308

304309
const bedrockFile = readYaml<IBedrockFile>(bedrockFilePath);
310+
305311
logger.info(`filejson: ${JSON.stringify(bedrockFile)}`);
306312
expect(bedrockFile.variableGroups![0]).toBe(variableGroupName);
307313
});
308314

309-
test("Should pass adding a valid variable group name when bedrock file exists when variableGroups length is already 1", async () => {
315+
test("Should pass adding a valid variable group name when bedrock file exists when variableGroups length is > 0", async () => {
310316
// Create random directory to initialize
311317
const randomTmpDir = path.join(os.tmpdir(), uuid());
312318
fs.mkdirSync(randomTmpDir);
@@ -332,3 +338,165 @@ describe("setVariableGroupInBedrockFile", () => {
332338
expect(bedrockFile.variableGroups![1]).toBe(variableGroupName);
333339
});
334340
});
341+
342+
describe("updateLifeCyclePipeline", () => {
343+
beforeAll(() => {
344+
mockFs({
345+
"bedrock.yaml": createTestBedrockYaml() as any
346+
});
347+
});
348+
349+
afterAll(() => {
350+
mockFs.restore();
351+
});
352+
353+
afterEach(() => {
354+
jest.clearAllMocks();
355+
});
356+
357+
test("Should fail with empty arguments", async () => {
358+
let invalidDirError: Error | undefined;
359+
try {
360+
await updateLifeCyclePipeline("");
361+
} catch (err) {
362+
invalidDirError = err;
363+
}
364+
expect(invalidDirError).toBeDefined();
365+
});
366+
367+
test("Should fail adding a variable group name when no pipeline yaml file exists", async () => {
368+
// Create random directory to initialize
369+
const randomTmpDir = path.join(os.tmpdir(), uuid());
370+
fs.mkdirSync(randomTmpDir);
371+
372+
let noFileError: Error | undefined;
373+
374+
try {
375+
await updateLifeCyclePipeline(randomTmpDir);
376+
} catch (err) {
377+
noFileError = err;
378+
}
379+
expect(noFileError).toBeDefined();
380+
});
381+
382+
test("Should pass adding variable groups when bedrock file exists with empty variableGroups", async () => {
383+
// Create random directory to initialize
384+
const randomTmpDir = path.join(os.tmpdir(), uuid());
385+
fs.mkdirSync(randomTmpDir);
386+
const writeSpy = jest.spyOn(fs, "writeFileSync");
387+
388+
const defaultBedrockFileObject = createTestBedrockYaml(
389+
false
390+
) as IBedrockFile;
391+
392+
write(defaultBedrockFileObject, randomTmpDir);
393+
394+
const hldFilePath = path.join(randomTmpDir, "hld-lifecycle.yaml");
395+
396+
const hldLifeCycleFile: IAzurePipelinesYaml = createTestHldLifecyclePipelineYaml(
397+
false
398+
) as IAzurePipelinesYaml;
399+
400+
const asYaml = yaml.safeDump(hldLifeCycleFile, {
401+
lineWidth: Number.MAX_SAFE_INTEGER
402+
});
403+
404+
fs.writeFileSync(hldFilePath, asYaml);
405+
406+
await updateLifeCyclePipeline(randomTmpDir);
407+
408+
const hldLifeCycleYaml = readYaml<IAzurePipelinesYaml>(hldFilePath);
409+
logger.info(`filejson: ${JSON.stringify(hldLifeCycleYaml)}`);
410+
expect(hldLifeCycleYaml.variables!.length).toBeLessThanOrEqual(0);
411+
});
412+
413+
test("Should pass adding variable groups when bedrock file exists with one variableGroup", async () => {
414+
// Create random directory to initialize
415+
const randomTmpDir = path.join(os.tmpdir(), uuid());
416+
fs.mkdirSync(randomTmpDir);
417+
logger.info(`random dir: ${randomTmpDir})`);
418+
419+
const defaultBedrockFileObject = createTestBedrockYaml(
420+
false
421+
) as IBedrockFile;
422+
423+
// add new variabe group
424+
defaultBedrockFileObject.variableGroups = [
425+
...(defaultBedrockFileObject.variableGroups ?? []),
426+
variableGroupName
427+
];
428+
429+
write(defaultBedrockFileObject, randomTmpDir);
430+
431+
const hldFilePath = path.join(randomTmpDir, "hld-lifecycle.yaml");
432+
433+
const hldLifeCycleFile: IAzurePipelinesYaml = createTestHldLifecyclePipelineYaml(
434+
false
435+
) as IAzurePipelinesYaml;
436+
437+
const asYaml = yaml.safeDump(hldLifeCycleFile, {
438+
lineWidth: Number.MAX_SAFE_INTEGER
439+
});
440+
441+
fs.writeFileSync(hldFilePath, asYaml);
442+
443+
await updateLifeCyclePipeline(randomTmpDir);
444+
445+
const hldLifeCycleYaml = readYaml<IAzurePipelinesYaml>(hldFilePath);
446+
logger.info(`filejson: ${JSON.stringify(hldLifeCycleYaml)}`);
447+
expect(hldLifeCycleYaml.variables![0]).toEqual({
448+
group: variableGroupName
449+
});
450+
});
451+
});
452+
453+
describe("isBedrockFileExists", () => {
454+
test("Should fail when empty file directory is passed", async () => {
455+
let invalidDirError: Error | undefined;
456+
457+
try {
458+
logger.info("calling create");
459+
await isBedrockFileExists("");
460+
} catch (err) {
461+
invalidDirError = err;
462+
}
463+
expect(invalidDirError).toBeDefined();
464+
});
465+
466+
test("Should return false when bedrock file does not exist", async () => {
467+
// Create random directory to initialize
468+
const randomTmpDir = path.join(os.tmpdir(), uuid());
469+
fs.mkdirSync(randomTmpDir);
470+
471+
const exists = await isBedrockFileExists(randomTmpDir);
472+
473+
logger.info(`bedrock.yaml file exists: ${exists}`);
474+
475+
expect(exists).toBe(false);
476+
});
477+
478+
test("Should return true when bedrock file exists", async () => {
479+
// Create random directory to initialize
480+
const randomTmpDir = path.join(os.tmpdir(), uuid());
481+
fs.mkdirSync(randomTmpDir);
482+
483+
logger.info(`random temp dir: ${randomTmpDir}`);
484+
485+
// create bedrock file to simulate the the use case that `spk project init` ran before
486+
const bedrockFileData: IBedrockFile = {
487+
rings: {},
488+
services: {},
489+
variableGroups: []
490+
};
491+
492+
const asYaml = yaml.safeDump(bedrockFileData, {
493+
lineWidth: Number.MAX_SAFE_INTEGER
494+
});
495+
fs.writeFileSync(path.join(randomTmpDir, "bedrock.yaml"), asYaml);
496+
497+
const exists = await isBedrockFileExists(randomTmpDir);
498+
logger.info(`bedrock.yaml file exists: ${exists} in ${randomTmpDir}`);
499+
500+
expect(exists).toBe(true);
501+
});
502+
});

0 commit comments

Comments
 (0)