Skip to content

Commit

Permalink
feat: More detailed error message upon ARM deployment failure (#306)
Browse files Browse the repository at this point in the history
Logs a more detailed error message when an ARM template deployment fails
  • Loading branch information
tbarlow12 committed Sep 13, 2019
1 parent aca4fd0 commit 4c7c5de
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 7 deletions.
6 changes: 6 additions & 0 deletions src/models/azureProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ export interface FunctionMetadata {
handler: string;
events: FunctionEvent[];
}

export interface DeploymentExtendedError {
code: string;
message: string;
details?: DeploymentExtendedError[];
}
43 changes: 43 additions & 0 deletions src/services/armService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import jsonpath from "jsonpath";
import { Deployments } from "@azure/arm-resources";
import { Deployment, DeploymentExtended } from "@azure/arm-resources/esm/models";
import { ResourceService } from "./resourceService";
import { DeploymentExtendedError } from "../models/azureProvider";

describe("Arm Service", () => {
let sls: Serverless
Expand Down Expand Up @@ -311,5 +312,47 @@ describe("Arm Service", () => {
expect(call[1]).toMatch(expectedDeploymentNameRegex);
expect(call[2]).toEqual(expectedDeployment);
});

it("Throws more detailed error message upon failed ARM deployment", async () => {
Deployments.prototype.createOrUpdate = jest.fn(() => Promise.reject(null));
const lastDeploymentError: DeploymentExtendedError = {
code: "DeploymentFailed",
message: "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-debug for usage details.",
details: [
{
code: "ServiceAlreadyExists",
message: "Api service already exists: abc-123-apim"
},
{
code: "StorageAccountAlreadyTaken",
message: "The storage account named ABC123 is already taken."
}
]
}
ResourceService.prototype.getLastDeployment = jest.fn(() => Promise.resolve({
properties: {
error: lastDeploymentError
}
})) as any;
const deployment: ArmDeployment = {
parameters: MockFactory.createTestParameters(false),
template: MockFactory.createTestArmTemplate()
};
deployment.parameters.param1 = "3"
const { code, message, details } = lastDeploymentError;
let errorPattern = [
code,
message,
details[0].code,
details[0].message,
details[1].code,
details[1].message
].join(".*")
await expect(service.deployTemplate(deployment))
.rejects
.toThrowError(
new RegExp(`.*${errorPattern}.*`,"s")
);
});
});
});
40 changes: 33 additions & 7 deletions src/services/armService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Guard } from "../shared/guard";
import { BaseService } from "./baseService";
import { ResourceService } from "./resourceService"
import deepEqual from "deep-equal";
import { DeploymentExtendedError } from "../models/azureProvider";

export class ArmService extends BaseService {
private resourceClient: ResourceManagementClient;
Expand Down Expand Up @@ -130,14 +131,39 @@ export class ArmService extends BaseService {
this.log(`---> Resource Group: ${this.resourceGroup}`)
this.log(`---> Deployment Name: ${this.deploymentName}`)

const result = await this.resourceClient.deployments.createOrUpdate(
this.resourceGroup,
this.deploymentName,
armDeployment
);
this.log("-> ARM deployment complete");
try {
const result = await this.resourceClient.deployments.createOrUpdate(
this.resourceGroup,
this.deploymentName,
armDeployment
);
this.log("-> ARM deployment complete");
return result;
} catch (err) {
const lastDeployment = await resourceService.getLastDeployment();
const errorDetails: DeploymentExtendedError = lastDeployment.properties["error"];
if (errorDetails) {
throw new Error(this.deploymentErrorToString(errorDetails));
}
}
}

return result;
private deploymentErrorToString(deploymentError: DeploymentExtendedError) {
if (!deploymentError.code || !deploymentError.message) {
return JSON.stringify(deploymentError);
}
let errorString = `${deploymentError.code} - ${deploymentError.message}`;
if (deploymentError.details) {

errorString += `
------------------------
DEPLOYMENT ERROR DETAILS
------------------------\n`
deploymentError.details.forEach((childError) => {
errorString += `\n${this.deploymentErrorToString(childError)}`
})
}
return errorString;
}

/**
Expand Down

0 comments on commit 4c7c5de

Please sign in to comment.