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

Commit 7b212c9

Browse files
[FEATURE] scaffold app and helm repo in spk setup command (#398)
* [FEATURE] scaffold app and helm repo in spk setup command * fix eslint error * making acr name configurable that's not hardcode it * added code to check if acr name is between 5 and 50 chars long * fix lint Co-authored-by: Nate <[email protected]>
1 parent 9018ade commit 7b212c9

19 files changed

+606
-55
lines changed

input1.txt

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
azdo_org_name=veseah
2+
azdo_project_name=SPK12345
3+
azdo_pat=doegx7fx26zj5gwgzlbz4rphh5h5dzwqelxaucibyvgxp45vhlna
4+
az_create_app=true
5+
az_create_sp=false
6+
az_sp_id=53441b1b-132e-4dba-992f-9009d86ddfa3
7+
az_sp_password=e79ff863-5b90-4340-b9e9-f7cede7a03bd
8+
az_sp_tenant=72f988bf-86f1-41af-91ab-2d7cd011db47
9+
az_subscription_id=dd831253-787f-4dc8-8eb0-ac9d052177d9
10+
az_acr_name=quickStartACR
11+

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ export const setVariableGroupInBedrockFile = (
233233
const bedrockFile = Bedrock(rootProjectPath);
234234

235235
if (typeof bedrockFile === "undefined") {
236-
throw new Error(`Bedrock file does not exist.`);
236+
throw Error(`Bedrock file does not exist.`);
237237
}
238238

239239
logger.verbose(
@@ -259,10 +259,10 @@ export const setVariableGroupInBedrockFile = (
259259
*/
260260
export const updateLifeCyclePipeline = (rootProjectPath: string): void => {
261261
if (!hasValue(rootProjectPath)) {
262-
throw new Error("Project root path is not valid");
262+
throw Error("Project root path is not valid");
263263
}
264264

265-
const fileName: string = PROJECT_PIPELINE_FILENAME;
265+
const fileName = PROJECT_PIPELINE_FILENAME;
266266
const absProjectRoot = path.resolve(rootProjectPath);
267267

268268
// Get bedrock.yaml

src/commands/setup.md

+9-4
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ for a few questions
2121
2. Subscription Id is automatically retrieved with the Service Principal
2222
credential. In case, there are two or more subscriptions, you will be
2323
prompt to select one of them.
24-
3. Create a resource group, `quick-start-rg` if it does not exist.
25-
4. Create a Azure Container Registry, `quickStartACR` in resource group,
26-
`quick-start-rg` if it does not exist.
2724

2825
It can also run in a non interactive mode by providing a file that contains
2926
answers to the above questions.
@@ -58,7 +55,15 @@ The followings shall be created
5855
already exists.
5956
1. And initial commit shall be made to this repo
6057
5. A High Level Definition (HLD) to Manifest pipeline.
61-
6. A Service Principal (if requested)
58+
6. If user chose to create sample app repo
59+
1. A Service Principal (if requested)
60+
2. A resource group, `quick-start-rg` if it does not exist.
61+
3. A Azure Container Registry, `quickStartACR` in resource group,
62+
`quick-start-rg` if it does not exist.
63+
4. A Git Repo, `quick-start-helm`, it shall be deleted and recreated if is
64+
already exists.
65+
5. A Git Repo, `quick-start-app`, it shall be deleted and recreated if is
66+
already exists.
6267

6368
## Setup log
6469

src/commands/setup.test.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,10 @@ const testExecuteFunc = async (
8080
// eslint-disable-next-line @typescript-eslint/no-explicit-any
8181
.mockReturnValueOnce(Promise.resolve({} as any));
8282
jest.spyOn(fsUtil, "createDirectory").mockReturnValueOnce();
83-
jest.spyOn(scaffold, "hldRepo").mockReturnValueOnce(Promise.resolve());
84-
jest.spyOn(scaffold, "manifestRepo").mockReturnValueOnce(Promise.resolve());
83+
jest.spyOn(scaffold, "hldRepo").mockResolvedValueOnce();
84+
jest.spyOn(scaffold, "manifestRepo").mockResolvedValueOnce();
85+
jest.spyOn(scaffold, "helmRepo").mockResolvedValueOnce();
86+
jest.spyOn(scaffold, "appRepo").mockResolvedValueOnce();
8587
jest
8688
.spyOn(pipelineService, "createHLDtoManifestPipeline")
8789
.mockReturnValueOnce(Promise.resolve());

src/commands/setup.ts

+42-28
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { create as createACR } from "../lib/azure/containerRegistryService";
77
import { create as createResourceGroup } from "../lib/azure/resourceService";
88
import { build as buildCmd, exit as exitCmd } from "../lib/commandBuilder";
99
import {
10-
ACR,
1110
RequestContext,
1211
RESOURCE_GROUP,
1312
RESOURCE_GROUP_LOCATION,
@@ -18,10 +17,16 @@ import { getGitApi } from "../lib/setup/gitService";
1817
import { createHLDtoManifestPipeline } from "../lib/setup/pipelineService";
1918
import { createProjectIfNotExist } from "../lib/setup/projectService";
2019
import { getAnswerFromFile, prompt } from "../lib/setup/prompt";
21-
import { hldRepo, manifestRepo } from "../lib/setup/scaffold";
20+
import {
21+
appRepo,
22+
helmRepo,
23+
hldRepo,
24+
manifestRepo
25+
} from "../lib/setup/scaffold";
2226
import { create as createSetupLog } from "../lib/setup/setupLog";
2327
import { logger } from "../logger";
2428
import decorator from "./setup.decorator.json";
29+
import { IGitApi } from "azure-devops-node-api/GitApi";
2530

2631
interface CommandOptions {
2732
file: string | undefined;
@@ -79,6 +84,40 @@ export const getErrorMessage = (
7984
return err.toString();
8085
};
8186

87+
export const createAppRepoTasks = async (
88+
gitAPI: IGitApi,
89+
rc: RequestContext
90+
): Promise<void> => {
91+
if (
92+
rc.toCreateAppRepo &&
93+
rc.servicePrincipalId &&
94+
rc.servicePrincipalPassword &&
95+
rc.servicePrincipalTenantId &&
96+
rc.subscriptionId &&
97+
rc.acrName
98+
) {
99+
rc.createdResourceGroup = await createResourceGroup(
100+
rc.servicePrincipalId,
101+
rc.servicePrincipalPassword,
102+
rc.servicePrincipalTenantId,
103+
rc.subscriptionId,
104+
RESOURCE_GROUP,
105+
RESOURCE_GROUP_LOCATION
106+
);
107+
rc.createdACR = await createACR(
108+
rc.servicePrincipalId,
109+
rc.servicePrincipalPassword,
110+
rc.servicePrincipalTenantId,
111+
rc.subscriptionId,
112+
RESOURCE_GROUP,
113+
rc.acrName,
114+
RESOURCE_GROUP_LOCATION
115+
);
116+
await helmRepo(gitAPI, rc);
117+
await appRepo(gitAPI, rc);
118+
}
119+
};
120+
82121
/**
83122
* Executes the command, can all exit function with 0 or 1
84123
* when command completed successfully or failed respectively.
@@ -106,32 +145,7 @@ export const execute = async (
106145
await hldRepo(gitAPI, requestContext);
107146
await manifestRepo(gitAPI, requestContext);
108147
await createHLDtoManifestPipeline(buildAPI, requestContext);
109-
110-
if (
111-
requestContext.toCreateAppRepo &&
112-
requestContext.servicePrincipalId &&
113-
requestContext.servicePrincipalPassword &&
114-
requestContext.servicePrincipalTenantId &&
115-
requestContext.subscriptionId
116-
) {
117-
requestContext.createdResourceGroup = await createResourceGroup(
118-
requestContext.servicePrincipalId,
119-
requestContext.servicePrincipalPassword,
120-
requestContext.servicePrincipalTenantId,
121-
requestContext.subscriptionId,
122-
RESOURCE_GROUP,
123-
RESOURCE_GROUP_LOCATION
124-
);
125-
requestContext.createdACR = await createACR(
126-
requestContext.servicePrincipalId,
127-
requestContext.servicePrincipalPassword,
128-
requestContext.servicePrincipalTenantId,
129-
requestContext.subscriptionId,
130-
RESOURCE_GROUP,
131-
ACR,
132-
RESOURCE_GROUP_LOCATION
133-
);
134-
}
148+
await createAppRepoTasks(gitAPI, requestContext);
135149

136150
createSetupLog(requestContext);
137151
await exitFn(0);

src/lib/azure/containerRegistryService.test.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { RegistriesCreateResponse } from "@azure/arm-containerregistry/src/models";
1+
import {
2+
RegistriesCreateResponse,
3+
RegistriesListResponse
4+
} from "@azure/arm-containerregistry/src/models";
25

36
import * as restAuth from "@azure/ms-rest-nodeauth";
47
import {
@@ -17,15 +20,15 @@ jest.mock("@azure/arm-containerregistry", () => {
1720
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1821
return {} as any;
1922
},
20-
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
21-
list: () => {
23+
list: (): Promise<RegistriesListResponse> => {
2224
return [
2325
{
2426
id:
2527
"/subscriptions/dd831253-787f-4dc8-8eb0-ac9d052177d9/resourceGroups/bedrockSPK/providers/Microsoft.ContainerRegistry/registries/acrWest",
2628
name: "acrWest"
2729
}
28-
];
30+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
31+
] as any;
2932
}
3033
}
3134
};

src/lib/azure/resourceService.test.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { ResourceGroupsCreateOrUpdateResponse } from "@azure/arm-resources/src/models";
1+
import {
2+
ResourceGroupsCreateOrUpdateResponse,
3+
ResourceGroupsListResponse
4+
} from "@azure/arm-resources/src/models";
25
import * as restAuth from "@azure/ms-rest-nodeauth";
36
import { create, getResourceGroups, isExist } from "./resourceService";
47
import * as resourceService from "./resourceService";
@@ -16,15 +19,15 @@ jest.mock("@azure/arm-resources", () => {
1619
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1720
return {} as any;
1821
},
19-
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
20-
list: () => {
22+
list: (): Promise<ResourceGroupsListResponse> => {
2123
return [
2224
{
2325
id: "1234567890-abcdef",
2426
location: RESOURCE_GROUP_LOCATION,
2527
name: "test"
2628
}
27-
];
29+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
30+
] as any;
2831
}
2932
}
3033
};

src/lib/pipelines/variableGroup.test.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import {
33
VariableGroupParameters
44
} from "azure-devops-node-api/interfaces/TaskAgentInterfaces";
55
import uuid from "uuid/v4";
6+
import * as azdoClient from "../azdoClient";
67
import { readYaml } from "../../config";
78
import * as config from "../../config";
8-
import * as azdoClient from "../azdoClient";
99
import {
1010
disableVerboseLogging,
1111
enableVerboseLogging,
@@ -17,6 +17,7 @@ import {
1717
addVariableGroupWithKeyVaultMap,
1818
authorizeAccessToAllPipelines,
1919
buildVariablesMap,
20+
deleteVariableGroup,
2021
doAddVariableGroup
2122
} from "./variableGroup";
2223

@@ -403,3 +404,32 @@ describe("buildVariablesMap", () => {
403404
expect(Object.keys(secretsMap).length).toBe(0);
404405
});
405406
});
407+
408+
describe("test deleteVariableGroup function", () => {
409+
it("positive test: group found", async () => {
410+
const delFn = jest.fn();
411+
jest.spyOn(azdoClient, "getTaskAgentApi").mockResolvedValue({
412+
deleteVariableGroup: delFn,
413+
getVariableGroups: () => [
414+
{
415+
id: "test"
416+
}
417+
]
418+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
419+
} as any);
420+
const deleted = await deleteVariableGroup({}, "test");
421+
expect(delFn).toBeCalledTimes(1);
422+
expect(deleted).toBeTruthy();
423+
});
424+
it("positive test: no matching groups found", async () => {
425+
const delFn = jest.fn();
426+
jest.spyOn(azdoClient, "getTaskAgentApi").mockResolvedValue({
427+
deleteVariableGroup: delFn,
428+
getVariableGroups: () => []
429+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
430+
} as any);
431+
const deleted = await deleteVariableGroup({}, "test");
432+
expect(delFn).toBeCalledTimes(0);
433+
expect(deleted).toBeFalsy();
434+
});
435+
});

src/lib/pipelines/variableGroup.ts

+23
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,26 @@ export const addVariableGroupWithKeyVaultMap = async (
238238
throw err;
239239
}
240240
};
241+
242+
/**
243+
* Deletes variable group
244+
*
245+
* @param opts optionally override spk config with Azure DevOps access options
246+
* @param name Name of group to be deleted.
247+
* @returns true if group exists and deleted.
248+
*/
249+
export const deleteVariableGroup = async (
250+
opts: AzureDevOpsOpts,
251+
name: string
252+
): Promise<boolean> => {
253+
const taskClient = await getTaskAgentApi(opts);
254+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
255+
const project = opts.project!;
256+
257+
const groups = await taskClient.getVariableGroups(project, name);
258+
if (groups && groups.length > 0 && groups[0].id) {
259+
await taskClient.deleteVariableGroup(project, groups[0].id);
260+
return true;
261+
}
262+
return false;
263+
};

src/lib/setup/constants.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ export interface RequestContext {
33
projectName: string;
44
accessToken: string;
55
workspace: string;
6+
acrName?: string;
67
toCreateAppRepo?: boolean;
78
toCreateSP?: boolean;
89
createdProject?: boolean;
910
scaffoldHLD?: boolean;
1011
scaffoldManifest?: boolean;
12+
scaffoldHelm?: boolean;
13+
scaffoldAppService?: boolean;
1114
createdHLDtoManifestPipeline?: boolean;
1215
createServicePrincipal?: boolean;
1316
servicePrincipalId?: string;
@@ -21,14 +24,16 @@ export interface RequestContext {
2124

2225
export const MANIFEST_REPO = "quick-start-manifest";
2326
export const HLD_REPO = "quick-start-hld";
27+
export const HELM_REPO = "quick-start-helm";
2428
export const APP_REPO = "quick-start-app";
2529
export const DEFAULT_PROJECT_NAME = "BedrockRocks";
2630
export const APP_REPO_LIFECYCLE = "quick-start-lifecycle";
2731
export const WORKSPACE = "quick-start-env";
2832
export const SP_USER_NAME = "service_account";
2933
export const RESOURCE_GROUP = "quick-start-rg";
3034
export const RESOURCE_GROUP_LOCATION = "westus2";
31-
export const ACR = "quickStartACR";
35+
export const ACR_NAME = "quickStartACR";
36+
export const VARIABLE_GROUP = "quick-start-vg";
3237
export const SETUP_LOG = "setup.log";
3338

3439
export const HLD_DEFAULT_GIT_URL =

0 commit comments

Comments
 (0)