Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add separate suite of tests for managed identity and admin credential deployments #789

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions test/global.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import * as assert from 'assert';
import * as vscode from 'vscode';
import { ext, registerOnActionStartHandler, registerUIExtensionVariables } from '../extension.bundle';

const longRunningLocalTestsEnabled: boolean = !/^(false|0)?$/i.test(process.env.AzCode_EnableLongRunningTestsLocal || '');
const longRunningRemoteTestsEnabled: boolean = !/^(false|0)?$/i.test(process.env.AzCode_UseAzureFederatedCredentials || '');
export const longRunningLocalTestsEnabled: boolean = !/^(false|0)?$/i.test(process.env.AzCode_EnableLongRunningTestsLocal || '');
export const longRunningRemoteTestsEnabled: boolean = !/^(false|0)?$/i.test(process.env.AzCode_UseAzureFederatedCredentials || '');

export const longRunningTestsEnabled: boolean = longRunningLocalTestsEnabled || longRunningRemoteTestsEnabled;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { workspace, type Uri, type WorkspaceFolder } from "vscode";
import { AzExtFsExtra, deployWorkspaceProject, dwpSettingUtilsV2, ext, parseError, settingUtils, type DeploymentConfigurationSettings, type DeployWorkspaceProjectResults, type IParsedError } from "../../../extension.bundle";
import { assertStringPropsMatch, getWorkspaceFolderUri } from "../../testUtils";
import { resourceGroupsToDelete } from "../global.nightly.test";
import { dwpTestScenarios, type DeployWorkspaceProjectTestScenario } from "./dwpTestScenarios";
import { getDwpTestScenarios, type DeployWorkspaceProjectTestScenario } from "./dwpTestScenarios";

export interface DwpParallelTestScenario {
title: string;
Expand All @@ -19,7 +19,7 @@ export interface DwpParallelTestScenario {
}

export function buildParallelTestScenarios(): DwpParallelTestScenario[] {
return dwpTestScenarios.map(scenario => {
return getDwpTestScenarios().map(scenario => {
return {
title: scenario.label,
callback: buildParallelScenarioCallback(scenario),
Expand Down
43 changes: 30 additions & 13 deletions test/nightly/deployWorkspaceProject/dwpTestScenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,42 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { longRunningLocalTestsEnabled } from "../../global.test";
import { generateAlbumApiJavaScriptTestCases } from "./testCases/albumApiJavaScriptTestCases";
import { type DeployWorkspaceProjectTestCase } from "./testCases/DeployWorkspaceProjectTestCase";
import { generateMonoRepoBasicTestCases } from "./testCases/monoRepoBasicTestCases";
import { generateMonoRepoAdminCredentialsTestCases } from "./testCases/monoRepoTestCases/adminCredentialsTestCases";
import { generateMonoRepoIdentityTestCases } from "./testCases/monoRepoTestCases/identityTestCases";

export interface DeployWorkspaceProjectTestScenario {
label: string;
folderName: string;
testCases: DeployWorkspaceProjectTestCase[];
}

export const dwpTestScenarios: DeployWorkspaceProjectTestScenario[] = [
{
label: 'albumapi-js',
folderName: 'albumapi-js',
testCases: generateAlbumApiJavaScriptTestCases()
},
{
label: 'monorepo',
folderName: 'monorepo-basic',
testCases: generateMonoRepoBasicTestCases()
},
];
export function getDwpTestScenarios(): DeployWorkspaceProjectTestScenario[] {
const dwpTestScenarios: DeployWorkspaceProjectTestScenario[] = [
{
label: 'albumapi-js',
folderName: 'albumapi-js',
testCases: generateAlbumApiJavaScriptTestCases(),
},
{
label: 'monorepo-admincreds',
folderName: 'monorepo-admincreds',
testCases: generateMonoRepoAdminCredentialsTestCases(),
},
];

if (longRunningLocalTestsEnabled) {
// Insufficient auth privilege to test managed identity / role assignment in our manual testing subscription.
// Therefore, limit these tests to only run locally in personal subscriptions where user has full permission to assign roles.
// Todo: Investigate if it makes sense to elevate remote privileges such that these tests can also be automated to run remotely.
dwpTestScenarios.push({
label: 'monorepo-identity',
folderName: 'monorepo-identity',
testCases: generateMonoRepoIdentityTestCases(),
});
}

return dwpTestScenarios;
}
16 changes: 15 additions & 1 deletion test/nightly/deployWorkspaceProject/dwpTestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { subscriptionContext } from "../global.nightly.test";
import { type PostTestAssertion } from "./testCases/DeployWorkspaceProjectTestCase";

export namespace dwpTestUtils {
export function generateExpectedResults(sharedResourceName: string, acrResourceName: string, appResourceName: string): StringOrRegExpProps<DeployWorkspaceProjectResults> {
export function generateExpectedResultsWithCredentials(sharedResourceName: string, acrResourceName: string, appResourceName: string): StringOrRegExpProps<DeployWorkspaceProjectResults> {
return {
containerAppId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.App\/containerApps\/${appResourceName}`, 'i'),
imageName: new RegExp(appResourceName, 'i'),
Expand All @@ -27,6 +27,20 @@ export namespace dwpTestUtils {
};
}

export function generateExpectedResultsWithoutCredentials(sharedResourceName: string, acrResourceName: string, appResourceName: string): StringOrRegExpProps<DeployWorkspaceProjectResults> {
return {
containerAppId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.App\/containerApps\/${appResourceName}`, 'i'),
imageName: new RegExp(appResourceName, 'i'),
logAnalyticsWorkspaceId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.OperationalInsights\/workspaces\/${sharedResourceName}`, 'i'),
managedEnvironmentId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.App\/managedEnvironments\/${sharedResourceName}`, 'i'),
registryId: new RegExp(`\/resourceGroups\/${sharedResourceName}\/providers\/Microsoft\.ContainerRegistry\/registries\/${acrResourceName}.{6}`, 'i'),
registryLoginServer: new RegExp(`${acrResourceName}.{6}\.azurecr\.io`, 'i'),
registryPassword: undefined,
registryUsername: undefined,
resourceGroupId: new RegExp(`\/resourceGroups\/${sharedResourceName}`, 'i')
};
}

export function generatePostTestAssertion(expectedContainerAppSettings: { targetPort: number | undefined, env: EnvironmentVar[] | undefined }): PostTestAssertion {
return async function postTestAssertion(context: IActionContext, resources: DeployWorkspaceProjectResults, errMsg?: string): Promise<void> {
const parsedId = parseAzureResourceId(nonNullProp(resources, 'containerAppId'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function generateAlbumApiJavaScriptTestCases(): DeployWorkspaceProjectTes
'East US',
'Save'
],
expectedResults: dwpTestUtils.generateExpectedResults(sharedResourceName, acrResourceName, appResourceName),
expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, appResourceName),
expectedVSCodeSettings: {
deploymentConfigurations: [
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, appResourceName)
Expand All @@ -68,7 +68,7 @@ export function generateAlbumApiJavaScriptTestCases(): DeployWorkspaceProjectTes
appResourceName,
'Continue'
],
expectedResults: dwpTestUtils.generateExpectedResults(sharedResourceName, acrResourceName, appResourceName),
expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, appResourceName),
expectedVSCodeSettings: {
deploymentConfigurations: [
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, appResourceName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@

import { randomUtils } from "@microsoft/vscode-azext-utils";
import * as path from "path";
import { type DeploymentConfigurationSettings } from "../../../../extension.bundle";
import { type StringOrRegExpProps } from "../../../typeUtils";
import { dwpTestUtils } from "../dwpTestUtils";
import { type DeployWorkspaceProjectTestCase } from "./DeployWorkspaceProjectTestCase";
import { dwpTestUtils } from "../../dwpTestUtils";
import { type DeployWorkspaceProjectTestCase } from "../DeployWorkspaceProjectTestCase";
import { generateExpectedDeploymentConfiguration } from "./generateExpectedDeploymentConfiguration";

export function generateMonoRepoBasicTestCases(): DeployWorkspaceProjectTestCase[] {
const folderName: string = 'monorepo-basic';
const sharedResourceName: string = 'monorepo-basic' + randomUtils.getRandomHexString(4);
export function generateMonoRepoAdminCredentialsTestCases(): DeployWorkspaceProjectTestCase[] {
const folderName: string = 'monorepo-admincreds';
const sharedResourceName: string = 'monorepo-ac' + randomUtils.getRandomHexString(4);
const acrResourceName: string = sharedResourceName.replace(/[^a-zA-Z0-9]+/g, '');

return [
Expand All @@ -32,7 +31,7 @@ export function generateMonoRepoBasicTestCases(): DeployWorkspaceProjectTestCase
'East US',
'Save'
],
expectedResults: dwpTestUtils.generateExpectedResults(sharedResourceName, acrResourceName, 'app1'),
expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, 'app1'),
expectedVSCodeSettings: {
deploymentConfigurations: [
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1')
Expand All @@ -55,7 +54,7 @@ export function generateMonoRepoBasicTestCases(): DeployWorkspaceProjectTestCase
path.join('app2', '.env.example'),
'Save'
],
expectedResults: dwpTestUtils.generateExpectedResults(sharedResourceName, acrResourceName, 'app2'),
expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, 'app2'),
expectedVSCodeSettings: {
deploymentConfigurations: [
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'),
Expand All @@ -78,7 +77,7 @@ export function generateMonoRepoBasicTestCases(): DeployWorkspaceProjectTestCase
path.join('app3', '.env.example'),
'Save'
],
expectedResults: dwpTestUtils.generateExpectedResults(sharedResourceName, acrResourceName, 'app3'),
expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, 'app3'),
expectedVSCodeSettings: {
deploymentConfigurations: [
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'),
Expand All @@ -95,7 +94,7 @@ export function generateMonoRepoBasicTestCases(): DeployWorkspaceProjectTestCase
'app1',
'Continue'
],
expectedResults: dwpTestUtils.generateExpectedResults(sharedResourceName, acrResourceName, 'app1'),
expectedResults: dwpTestUtils.generateExpectedResultsWithCredentials(sharedResourceName, acrResourceName, 'app1'),
expectedVSCodeSettings: {
deploymentConfigurations: [
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'),
Expand All @@ -107,16 +106,3 @@ export function generateMonoRepoBasicTestCases(): DeployWorkspaceProjectTestCase
}
];
}

function generateExpectedDeploymentConfiguration(sharedResourceName: string, acrResourceName: string, appResourceName: string): StringOrRegExpProps<DeploymentConfigurationSettings> {
return {
label: appResourceName,
type: 'AcrDockerBuildRequest',
dockerfilePath: path.join(appResourceName, 'Dockerfile'),
srcPath: appResourceName,
envPath: path.join(appResourceName, '.env.example'),
resourceGroup: sharedResourceName,
containerApp: appResourceName,
containerRegistry: new RegExp(`${acrResourceName}.{6}`, 'i'),
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as path from "path";
import { type DeploymentConfigurationSettings } from "../../../../../extension.bundle";
import { type StringOrRegExpProps } from "../../../../typeUtils";

export function generateExpectedDeploymentConfiguration(sharedResourceName: string, acrResourceName: string, appResourceName: string): StringOrRegExpProps<DeploymentConfigurationSettings> {
return {
label: appResourceName,
type: 'AcrDockerBuildRequest',
dockerfilePath: path.join(appResourceName, 'Dockerfile'),
srcPath: appResourceName,
envPath: path.join(appResourceName, '.env.example'),
resourceGroup: sharedResourceName,
containerApp: appResourceName,
containerRegistry: new RegExp(`${acrResourceName}.{6}`, 'i'),
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { randomUtils } from "@microsoft/vscode-azext-utils";
import * as path from "path";
import { dwpTestUtils } from "../../dwpTestUtils";
import { type DeployWorkspaceProjectTestCase } from "../DeployWorkspaceProjectTestCase";
import { generateExpectedDeploymentConfiguration } from "./generateExpectedDeploymentConfiguration";

export function generateMonoRepoIdentityTestCases(): DeployWorkspaceProjectTestCase[] {
const folderName: string = 'monorepo-identity';
const sharedResourceName: string = 'monorepo-id' + randomUtils.getRandomHexString(4);
const acrResourceName: string = sharedResourceName.replace(/[^a-zA-Z0-9]+/g, '');

return [
{
label: "Deploy App 1",
inputs: [
new RegExp(folderName, 'i'),
path.join('app1', 'Dockerfile'),
new RegExp('Create new container apps environment', 'i'),
'Continue',
sharedResourceName,
'app1',
`.${path.sep}app1`,
'Managed Identity',
path.join('app1', '.env.example'),
'East US',
'Save'
],
expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, 'app1'),
expectedVSCodeSettings: {
deploymentConfigurations: [
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1')
]
},
postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3000, env: [{ name: 'MESSAGE', value: 'container apps (app1)' }] }),
resourceGroupToDelete: sharedResourceName
},
{
label: "Deploy App 2",
inputs: [
new RegExp(folderName, 'i'),
new RegExp('Create and deploy new app configuration', 'i'),
path.join('app2', 'Dockerfile'),
new RegExp('(Recommended)', 'i'), // Select a container app environment
'Continue',
'app2',
`.${path.sep}app2`,
'Managed Identity',
path.join('app2', '.env.example'),
'Save'
],
expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, 'app2'),
expectedVSCodeSettings: {
deploymentConfigurations: [
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'),
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app2'),
]
},
postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3001, env: [{ name: 'MESSAGE', value: 'container apps (app2)' }] })
},
{
label: "Deploy App 3",
inputs: [
new RegExp(folderName, 'i'),
new RegExp('Create and deploy new app configuration', 'i'),
path.join('app3', 'Dockerfile'),
new RegExp('(Recommended)', 'i'), // Select a container app environment
'Continue',
'app3',
`.${path.sep}app3`,
'Managed Identity',
path.join('app3', '.env.example'),
'Save'
],
expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, 'app3'),
expectedVSCodeSettings: {
deploymentConfigurations: [
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'),
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app2'),
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app3'),
]
},
postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3002, env: [{ name: 'MESSAGE', value: 'container apps (app3)' }] }),
},
{
label: "Re-deploy App 1",
inputs: [
new RegExp(folderName, 'i'),
'app1',
'Continue'
],
expectedResults: dwpTestUtils.generateExpectedResultsWithoutCredentials(sharedResourceName, acrResourceName, 'app1'),
expectedVSCodeSettings: {
deploymentConfigurations: [
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app1'),
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app2'),
generateExpectedDeploymentConfiguration(sharedResourceName, acrResourceName, 'app3'),
]
},
postTestAssertion: dwpTestUtils.generatePostTestAssertion({ targetPort: 3000, env: [{ name: 'MESSAGE', value: 'container apps (app1)' }] })
}
];
}
8 changes: 6 additions & 2 deletions test/test.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
"path": "./testProjects/dockerfiles"
},
{
"name": "monorepo-basic",
"path": "./testProjects/monorepo-basic"
"name": "monorepo-admincreds",
"path": "./testProjects/monorepo-basic-1"
},
{
"name": "monorepo-identity",
"path": "./testProjects/monorepo-basic-2"
}
]
}
2 changes: 2 additions & 0 deletions test/testProjects/monorepo-basic-2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.vscode
3 changes: 3 additions & 0 deletions test/testProjects/monorepo-basic-2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Monorepo-basic

This extremely minimal Node.js monorepo is used for quickly testing simple scenarios in the Azure Container Apps Extension.
1 change: 1 addition & 0 deletions test/testProjects/monorepo-basic-2/app1/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MESSAGE='container apps (app1)'
6 changes: 6 additions & 0 deletions test/testProjects/monorepo-basic-2/app1/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM node:lts-alpine

COPY . /src
RUN cd /src && npm install
EXPOSE 3000
CMD ["node", "/src/server.js"]
9 changes: 9 additions & 0 deletions test/testProjects/monorepo-basic-2/app1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "aca-hello-world-app1",
"version": "1.0.0",
"description": "Simple test app for the ACA tools for VS Code extension",
"main": "server.js",
"scripts": {
"start": "node server.js"
}
}
12 changes: 12 additions & 0 deletions test/testProjects/monorepo-basic-2/app1/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const http = require('http');

const port = process.env.PORT || 3000;
const message = process.env.MESSAGE || 'world';

const server = http.createServer((_, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write(`Hello ${message}\n`);
});

server.listen(port);
console.log(`Server running at http://localhost: ${port}`);
1 change: 1 addition & 0 deletions test/testProjects/monorepo-basic-2/app2/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MESSAGE='container apps (app2)'
6 changes: 6 additions & 0 deletions test/testProjects/monorepo-basic-2/app2/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM node:lts-alpine

COPY . /src
RUN cd /src && npm install
EXPOSE 3001
CMD ["node", "/src/server.js"]
Loading
Loading