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

Commit be744de

Browse files
dennisseahandrebriggsNathanielRose
authored
[FEATURE] Add lifecycle and build pipelines in spk setup command (#420)
* [FEATURE] scaffold app and helm repo in spk setup command * [FEATURE] lifecycle and build pipeline in spk setup command * fix eslint error * fixed unit test * making acr name configurable that's not hardcode it * added code to check if acr name is between 5 and 50 chars long * Changes to get build pipeline working in branch issue1113x (#402) * Changes to get build pipeline working * Updated spelling * minor fixes * fix test * fix lint * fix tests * adding tests and prompt for subscriptionId before creating sp * rebase * fix eslint * Delete tslint.json * Update subscriptionService.test.ts * fixing labels and messages Co-authored-by: Andre Briggs <[email protected]> Co-authored-by: Nate <[email protected]>
1 parent fa89421 commit be744de

23 files changed

+643
-181
lines changed

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@
5252
"webpack-cli": "^3.3.9"
5353
},
5454
"prettier": {
55-
"proseWrap": "always",
56-
"quote-props": "preserve"
55+
"proseWrap": "always"
5756
},
5857
"husky": {
5958
"hooks": {

src/commands/deployment/dashboard.test.ts

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable @typescript-eslint/camelcase */
21
jest.mock("open");
32
import open from "open";
43
jest.mock("../../config");
@@ -31,18 +30,18 @@ afterAll(() => {
3130

3231
const mockConfig = (): void => {
3332
(Config as jest.Mock).mockReturnValueOnce({
34-
azure_devops: {
35-
access_token: uuid(),
33+
"azure_devops": {
34+
"access_token": uuid(),
3635
org: uuid(),
3736
project: uuid()
3837
},
3938
introspection: {
4039
azure: {
41-
account_name: uuid(),
40+
"account_name": uuid(),
4241
key: uuid(),
43-
partition_key: uuid(),
44-
source_repo_access_token: "test_token",
45-
table_name: uuid()
42+
"partition_key": uuid(),
43+
"source_repo_access_token": "test_token",
44+
"table_name": uuid()
4645
}
4746
}
4847
});
@@ -209,8 +208,8 @@ describe("Fallback to azure devops access token", () => {
209208
describe("Extract manifest repository information", () => {
210209
test("Manifest repository information is successfully extracted", () => {
211210
(Config as jest.Mock).mockReturnValue({
212-
azure_devops: {
213-
manifest_repository:
211+
"azure_devops": {
212+
"manifest_repository":
214213
"https://dev.azure.com/bhnook/fabrikam/_git/materialized"
215214
}
216215
});

src/commands/deployment/validate.test.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/* eslint-disable @typescript-eslint/camelcase */
2-
// imports
32
import uuid from "uuid/v4";
43
import * as deploymenttable from "../../lib/azure/deploymenttable";
54
import {

src/commands/infra/generate.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -336,14 +336,14 @@ export const generateConfigWithParentEqProjectPath = async (
336336
writeTfvarsFile(spkTfvarsObject, parentDirectory, SPK_TFVARS);
337337
await copyTfTemplate(templatePath, parentDirectory, true);
338338
} else {
339-
logger.warning(`Variables are not defined in the definition.yaml`);
339+
logger.warn(`Variables are not defined in the definition.yaml`);
340340
}
341341
if (parentInfraConfig.backend) {
342342
const backendTfvarsObject = generateTfvars(parentInfraConfig.backend);
343343
checkTfvars(parentDirectory, BACKEND_TFVARS);
344344
writeTfvarsFile(backendTfvarsObject, parentDirectory, BACKEND_TFVARS);
345345
} else {
346-
logger.warning(
346+
logger.warn(
347347
`A remote backend configuration is not defined in the definition.yaml`
348348
);
349349
}

src/commands/setup.md

+7-5
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ for a few questions
1616
4. To create a sample application Repo
1717
1. If Yes, a Azure Service Principal is needed. You have 2 options
1818
1. have the command line tool to create it. Azure command line tool shall
19-
be used
20-
2. provide the Service Principal Id, Password and Tenant Id.
21-
2. Subscription Id is automatically retrieved with the Service Principal
22-
credential. In case, there are two or more subscriptions, you will be
23-
prompt to select one of them.
19+
be used. You will be prompted to select a subscription identifier.
20+
2. Provide the Service Principal Id, Password, and Tenant Id. From this
21+
information, the tool will retrieve the subscription identifier.
2422

2523
It can also run in a non interactive mode by providing a file that contains
2624
answers to the above questions.
@@ -40,6 +38,8 @@ az_create_sp=<true to have command line to create service principal>
4038
az_sp_id=<sevice principal Id need if az_create_app=true and az_create_sp=false>
4139
az_sp_password=<sevice principal password need if az_create_app=true and az_create_sp=false>
4240
az_sp_tenant=<sevice principal tenant Id need if az_create_app=true and az_create_sp=false>
41+
az_subscription_id=<subscription id>
42+
az_acr_name=<name of azure container registry>
4343
```
4444

4545
`azdo_project_name` is optional and default value is `BedrockRocks`.
@@ -64,6 +64,8 @@ The followings shall be created
6464
already exists.
6565
5. A Git Repo, `quick-start-app`, it shall be deleted and recreated if is
6666
already exists.
67+
6. A Lifecycle pipeline.
68+
7. A Build pipeline.
6769

6870
## Setup log
6971

src/commands/setup.test.ts

+54-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ import * as scaffold from "../lib/setup/scaffold";
1616
import * as setupLog from "../lib/setup/setupLog";
1717
import { deepClone } from "../lib/util";
1818
import { ConfigYaml } from "../types";
19-
import { createSPKConfig, execute, getErrorMessage } from "./setup";
19+
import {
20+
createAppRepoTasks,
21+
createSPKConfig,
22+
execute,
23+
getErrorMessage
24+
} from "./setup";
2025
import * as setup from "./setup";
2126

2227
const mockRequestContext: RequestContext = {
@@ -83,11 +88,9 @@ const testExecuteFunc = async (
8388
jest.spyOn(fsUtil, "createDirectory").mockReturnValueOnce();
8489
jest.spyOn(scaffold, "hldRepo").mockResolvedValueOnce();
8590
jest.spyOn(scaffold, "manifestRepo").mockResolvedValueOnce();
86-
jest.spyOn(scaffold, "helmRepo").mockResolvedValueOnce();
87-
jest.spyOn(scaffold, "appRepo").mockResolvedValueOnce();
8891
jest
8992
.spyOn(pipelineService, "createHLDtoManifestPipeline")
90-
.mockReturnValueOnce(Promise.resolve());
93+
.mockResolvedValueOnce();
9194
jest.spyOn(resourceService, "create").mockResolvedValue(true);
9295
jest.spyOn(azureContainerRegistryService, "create").mockResolvedValue(true);
9396
jest.spyOn(setupLog, "create").mockReturnValueOnce();
@@ -309,3 +312,50 @@ describe("test getErrorMessage function", () => {
309312
);
310313
});
311314
});
315+
316+
const testCreateAppRepoTasks = async (prApproved = true): Promise<void> => {
317+
const mockRc: RequestContext = {
318+
orgName: "org",
319+
projectName: "project",
320+
accessToken: "pat",
321+
toCreateAppRepo: true,
322+
servicePrincipalId: "fakeId",
323+
servicePrincipalPassword: "fakePassword",
324+
servicePrincipalTenantId: "tenant",
325+
subscriptionId: "12344",
326+
acrName: "acr",
327+
workspace: "dummy"
328+
};
329+
330+
jest.spyOn(resourceService, "create").mockResolvedValueOnce(true);
331+
jest
332+
.spyOn(azureContainerRegistryService, "create")
333+
.mockResolvedValueOnce(true);
334+
jest.spyOn(scaffold, "helmRepo").mockResolvedValueOnce();
335+
jest.spyOn(scaffold, "appRepo").mockResolvedValueOnce();
336+
jest
337+
.spyOn(pipelineService, "createLifecyclePipeline")
338+
.mockResolvedValueOnce();
339+
jest
340+
.spyOn(promptInstance, "promptForApprovingHLDPullRequest")
341+
.mockResolvedValueOnce(prApproved);
342+
if (prApproved) {
343+
jest.spyOn(pipelineService, "createBuildPipeline").mockResolvedValueOnce();
344+
}
345+
346+
const res = await createAppRepoTasks(
347+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
348+
{} as any, // gitAPI
349+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
350+
{} as any, // buildAPI
351+
mockRc
352+
);
353+
expect(res).toBe(prApproved);
354+
};
355+
356+
describe("test createAppRepoTasks function", () => {
357+
it("positive test", async () => {
358+
await testCreateAppRepoTasks();
359+
await testCreateAppRepoTasks(false);
360+
});
361+
});

src/commands/setup.ts

+35-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/* eslint-disable @typescript-eslint/camelcase */
2+
import { IBuildApi } from "azure-devops-node-api/BuildApi";
3+
import { IGitApi } from "azure-devops-node-api/GitApi";
24
import commander from "commander";
35
import fs from "fs";
46
import yaml from "js-yaml";
@@ -15,9 +17,17 @@ import {
1517
} from "../lib/setup/constants";
1618
import { createDirectory } from "../lib/setup/fsUtil";
1719
import { getGitApi } from "../lib/setup/gitService";
18-
import { createHLDtoManifestPipeline } from "../lib/setup/pipelineService";
20+
import {
21+
createBuildPipeline,
22+
createHLDtoManifestPipeline,
23+
createLifecyclePipeline
24+
} from "../lib/setup/pipelineService";
1925
import { createProjectIfNotExist } from "../lib/setup/projectService";
20-
import { getAnswerFromFile, prompt } from "../lib/setup/prompt";
26+
import {
27+
getAnswerFromFile,
28+
prompt,
29+
promptForApprovingHLDPullRequest
30+
} from "../lib/setup/prompt";
2131
import {
2232
appRepo,
2333
helmRepo,
@@ -27,7 +37,6 @@ import {
2737
import { create as createSetupLog } from "../lib/setup/setupLog";
2838
import { logger } from "../logger";
2939
import decorator from "./setup.decorator.json";
30-
import { IGitApi } from "azure-devops-node-api/GitApi";
3140

3241
interface CommandOptions {
3342
file: string | undefined;
@@ -87,8 +96,9 @@ export const getErrorMessage = (
8796

8897
export const createAppRepoTasks = async (
8998
gitAPI: IGitApi,
99+
buildAPI: IBuildApi,
90100
rc: RequestContext
91-
): Promise<void> => {
101+
): Promise<boolean> => {
92102
if (
93103
rc.toCreateAppRepo &&
94104
rc.servicePrincipalId &&
@@ -116,6 +126,18 @@ export const createAppRepoTasks = async (
116126
);
117127
await helmRepo(gitAPI, rc);
118128
await appRepo(gitAPI, rc);
129+
await createLifecyclePipeline(buildAPI, rc);
130+
const approved = await promptForApprovingHLDPullRequest(rc);
131+
132+
if (approved) {
133+
await createBuildPipeline(buildAPI, rc);
134+
return true;
135+
}
136+
137+
logger.warn("HLD Pull Request is not approved.");
138+
return false;
139+
} else {
140+
return false;
119141
}
120142
};
121143

@@ -134,21 +156,23 @@ export const execute = async (
134156

135157
try {
136158
requestContext = opts.file ? getAnswerFromFile(opts.file) : await prompt();
159+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
160+
const rc = requestContext!;
137161
createDirectory(WORKSPACE, true);
138-
createSPKConfig(requestContext);
162+
createSPKConfig(rc);
139163

140164
const webAPI = await getWebApi();
141165
const coreAPI = await webAPI.getCoreApi();
142166
const gitAPI = await getGitApi(webAPI);
143167
const buildAPI = await getBuildApi();
144168

145-
await createProjectIfNotExist(coreAPI, requestContext);
146-
await hldRepo(gitAPI, requestContext);
147-
await manifestRepo(gitAPI, requestContext);
148-
await createHLDtoManifestPipeline(buildAPI, requestContext);
149-
await createAppRepoTasks(gitAPI, requestContext);
169+
await createProjectIfNotExist(coreAPI, rc);
170+
await hldRepo(gitAPI, rc);
171+
await manifestRepo(gitAPI, rc);
172+
await createHLDtoManifestPipeline(buildAPI, rc);
173+
await createAppRepoTasks(gitAPI, buildAPI, rc);
150174

151-
createSetupLog(requestContext);
175+
createSetupLog(rc);
152176
await exitFn(0);
153177
} catch (err) {
154178
const msg = getErrorMessage(requestContext, err);

src/lib/azure/containerRegistryService.test.ts

+38-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as restAuth from "@azure/ms-rest-nodeauth";
77
import {
88
create,
99
getContainerRegistries,
10+
getContainerRegistry,
1011
isExist
1112
} from "./containerRegistryService";
1213
import * as containerRegistryService from "./containerRegistryService";
@@ -109,7 +110,6 @@ describe("test container registries function", () => {
109110
servicePrincipalPassword,
110111
servicePrincipalTenantId,
111112
subscriptionId,
112-
RESOURCE_GROUP,
113113
"test"
114114
);
115115
expect(res).toBeTruthy();
@@ -123,7 +123,6 @@ describe("test container registries function", () => {
123123
servicePrincipalPassword,
124124
servicePrincipalTenantId,
125125
subscriptionId,
126-
RESOURCE_GROUP,
127126
"test"
128127
);
129128
expect(res).toBeFalsy();
@@ -143,7 +142,6 @@ describe("test container registries function", () => {
143142
servicePrincipalPassword,
144143
servicePrincipalTenantId,
145144
subscriptionId,
146-
RESOURCE_GROUP,
147145
"test"
148146
);
149147
expect(res).toBeFalsy();
@@ -177,3 +175,40 @@ describe("test container registries function", () => {
177175
expect(created).toBeTruthy();
178176
});
179177
});
178+
179+
describe("test getContainerRegistry function", () => {
180+
it("match", async () => {
181+
const entry = {
182+
id:
183+
"/subscriptions/dd831253-787f-4dc8-8eb0-ac9d052177d9/resourceGroups/quick-start-rg/providers/Microsoft.ContainerRegistry/registries/quickStartACR",
184+
name: "quickStartACR",
185+
resourceGroup: "quick-start-rg"
186+
};
187+
jest
188+
.spyOn(containerRegistryService, "getContainerRegistries")
189+
.mockResolvedValueOnce([entry]);
190+
const reg = await getContainerRegistry(
191+
servicePrincipalId,
192+
servicePrincipalPassword,
193+
servicePrincipalTenantId,
194+
subscriptionId,
195+
RESOURCE_GROUP,
196+
"quickStartACR"
197+
);
198+
expect(reg).toStrictEqual(entry);
199+
});
200+
it("no matches", async () => {
201+
jest
202+
.spyOn(containerRegistryService, "getContainerRegistries")
203+
.mockResolvedValueOnce([]);
204+
const reg = await getContainerRegistry(
205+
servicePrincipalId,
206+
servicePrincipalPassword,
207+
servicePrincipalTenantId,
208+
subscriptionId,
209+
RESOURCE_GROUP,
210+
"quickStartACR"
211+
);
212+
expect(reg).toBeUndefined();
213+
});
214+
});

0 commit comments

Comments
 (0)