This repository was archived by the owner on Dec 9, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 160
fix: Add retry support when listing deployed functions #215
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
fa06583
Added retry logic in function app listing
wbreza 4aaa14c
Updated app fucntion list log error message
wbreza c5b28b6
fix: Fail if unable to retrieve list of functions
wbreza 6cdc50e
fix: Moved retryCount and retryInterval into constants
wbreza a51424b
test: Verify Utils.wait(...)
wbreza File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,8 +10,11 @@ import { Guard } from "../shared/guard"; | |
| import { ArmService } from "./armService"; | ||
| import { AzureBlobStorageService } from "./azureBlobStorageService"; | ||
| import { BaseService } from "./baseService"; | ||
| import { Utils } from "../shared/utils"; | ||
|
|
||
| export class FunctionAppService extends BaseService { | ||
| private static readonly retryCount: number = 10; | ||
| private static readonly retryInterval: number = 5000; | ||
| private webClient: WebSiteManagementClient; | ||
| private blobService: AzureBlobStorageService; | ||
|
|
||
|
|
@@ -90,27 +93,53 @@ export class FunctionAppService extends BaseService { | |
| Guard.null(functionApp); | ||
|
|
||
| const getTokenUrl = `${this.baseUrl}${functionApp.id}/functions?api-version=2016-08-01`; | ||
| const response = await this.sendApiRequest("GET", getTokenUrl); | ||
| try { | ||
| const response = await Utils.runWithRetry(async () => { | ||
| const listFunctionsResponse = await this.sendApiRequest("GET", getTokenUrl); | ||
|
|
||
| if (response.status !== 200) { | ||
| return []; | ||
| } | ||
| if (listFunctionsResponse.status !== 200 || listFunctionsResponse.data.value.length === 0) { | ||
| this.log("-> Function App not ready. Retrying..."); | ||
| throw new Error(listFunctionsResponse.data); | ||
| } | ||
|
|
||
| return listFunctionsResponse; | ||
| }, FunctionAppService.retryCount, FunctionAppService.retryInterval); | ||
|
|
||
| return response.data.value.map((functionConfig) => functionConfig.properties); | ||
| return response.data.value.map((functionConfig) => functionConfig.properties); | ||
| } | ||
| catch (e) { | ||
| this.log("-> Unable to retrieve function app list"); | ||
| throw e; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Gets the configuration of the specified function within the function app | ||
| * @param functionApp The parent function app | ||
| * @param functionName The name of hte function | ||
| */ | ||
| public async getFunction(functionApp: Site, functionName: string): Promise<FunctionEnvelope> { | ||
| Guard.null(functionApp); | ||
| Guard.empty(functionName); | ||
|
|
||
| const getFunctionUrl = `${this.baseUrl}${functionApp.id}/functions/${functionName}?api-version=2016-08-01`; | ||
| const response = await this.sendApiRequest("GET", getFunctionUrl); | ||
|
|
||
| if (response.status !== 200) { | ||
| try { | ||
| const response = await Utils.runWithRetry(async () => { | ||
| const getFunctionResponse = await this.sendApiRequest("GET", getFunctionUrl); | ||
|
|
||
| if (getFunctionResponse.status !== 200) { | ||
| this.log("-> Function app not ready. Retrying...") | ||
| throw new Error(response.data); | ||
| } | ||
|
|
||
| return getFunctionResponse; | ||
| }, FunctionAppService.retryCount, FunctionAppService.retryInterval); | ||
|
|
||
| return response.data.properties; | ||
| } catch (e) { | ||
| return null; | ||
| } | ||
|
|
||
| return response.data.properties; | ||
| } | ||
|
|
||
| public async uploadFunctions(functionApp: Site): Promise<any> { | ||
|
|
@@ -122,6 +151,23 @@ export class FunctionAppService extends BaseService { | |
| const uploadFunctionApp = this.uploadZippedArfifactToFunctionApp(functionApp, functionZipFile); | ||
| const uploadBlobStorage = this.uploadZippedArtifactToBlobStorage(functionZipFile); | ||
| await Promise.all([uploadFunctionApp, uploadBlobStorage]); | ||
|
|
||
|
|
||
| this.log("Deployed serverless functions:") | ||
| const serverlessFunctions = this.serverless.service.getAllFunctions(); | ||
| const deployedFunctions = await this.listFunctions(functionApp); | ||
|
|
||
| // List functions that are part of the serverless yaml config | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will there ever be a use case where there are deployed functions that are not in the yamL? |
||
| deployedFunctions.forEach((functionConfig) => { | ||
| if (serverlessFunctions.includes(functionConfig.name)) { | ||
| const httpConfig = this.getFunctionHttpTriggerConfig(functionApp, functionConfig); | ||
|
|
||
| if (httpConfig) { | ||
| const method = httpConfig.methods[0].toUpperCase(); | ||
| this.log(`-> ${functionConfig.name}: ${method} ${httpConfig.url}`); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -166,23 +212,7 @@ export class FunctionAppService extends BaseService { | |
| }; | ||
|
|
||
| await this.sendFile(requestOptions, functionZipFile); | ||
|
|
||
| this.log("-> Function package uploaded successfully"); | ||
| const serverlessFunctions = this.serverless.service.getAllFunctions(); | ||
| const deployedFunctions = await this.listFunctions(functionApp); | ||
|
|
||
| this.log("Deployed serverless functions:") | ||
| deployedFunctions.forEach((functionConfig) => { | ||
| // List functions that are part of the serverless yaml config | ||
| if (serverlessFunctions.includes(functionConfig.name)) { | ||
| const httpConfig = this.getFunctionHttpTriggerConfig(functionApp, functionConfig); | ||
|
|
||
| if (httpConfig) { | ||
| const method = httpConfig.methods[0].toUpperCase(); | ||
| this.log(`-> ${functionConfig.name}: ${method} ${httpConfig.url}`); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.