From 951dde1d0f57e36c8f41dcd22293b8e709f24dea Mon Sep 17 00:00:00 2001 From: Cephas Lin Date: Fri, 6 Oct 2023 13:29:03 +0200 Subject: [PATCH 01/11] remove infra-related files --- .github/workflows/azure-dev.yaml | 61 -- .vscode/launch.json | 25 - azure.yaml | 33 - azureproject/production.py | 18 +- infra/appinsights.bicep | 1244 ------------------------------ infra/main.bicep | 48 -- infra/main.parameters.json | 18 - infra/resources.bicep | 283 ------- startup.sh | 4 - 9 files changed, 9 insertions(+), 1725 deletions(-) delete mode 100644 .github/workflows/azure-dev.yaml delete mode 100644 .vscode/launch.json delete mode 100644 azure.yaml delete mode 100644 infra/appinsights.bicep delete mode 100644 infra/main.bicep delete mode 100644 infra/main.parameters.json delete mode 100644 infra/resources.bicep delete mode 100644 startup.sh diff --git a/.github/workflows/azure-dev.yaml b/.github/workflows/azure-dev.yaml deleted file mode 100644 index 2cc1938e..00000000 --- a/.github/workflows/azure-dev.yaml +++ /dev/null @@ -1,61 +0,0 @@ -name: Azure Developer CLI - -on: - workflow_dispatch: - push: - branches: - - main - -permissions: - id-token: write - contents: read - -jobs: - build: - runs-on: ubuntu-latest - container: - image: mcr.microsoft.com/azure-dev-cli-apps:latest - env: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Log in with Azure (Federated Credentials) - if: ${{ env.AZURE_CLIENT_ID != '' }} - run: | - azd login ` - --client-id "$Env:AZURE_CLIENT_ID" ` - --federated-credential-provider "github" ` - --tenant-id "$Env:AZURE_TENANT_ID" - shell: pwsh - - - name: Log in with Azure (Client Credentials) - if: ${{ env.AZURE_CREDENTIALS != '' }} - run: | - $info = $Env:AZURE_CREDENTIALS | ConvertFrom-Json -AsHashtable; - Write-Host "::add-mask::$($info.clientSecret)" - azd login ` - --client-id "$($info.clientId)" ` - --client-secret "$($info.clientSecret)" ` - --tenant-id "$($info.tenantId)" - shell: pwsh - env: - AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} - - - name: Azure Dev Provision - run: azd provision --no-prompt - env: - AZURE_ENV_NAME: ${{ secrets.AZURE_ENV_NAME }} - AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - - name: Azure Dev Deploy - run: azd deploy --no-prompt - env: - AZURE_ENV_NAME: ${{ secrets.AZURE_ENV_NAME }} - AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index b1d7f90c..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Run server", - "type": "python", - "request": "launch", - "module": "flask", - "env": { - "FLASK_APP": "app.py", - "FLASK_DEBUG": "1" - }, - "args": [ - "run", - "--no-debugger", - "--no-reload" - ], - "jinja": true, - "justMyCode": true - } - ] -} diff --git a/azure.yaml b/azure.yaml deleted file mode 100644 index 4c911cbe..00000000 --- a/azure.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json - -name: flask-postgresql-sample-app -metadata: - template: flask-postgresql-sample-app@0.0.1-beta -services: - web: - project: . - language: py - host: appservice -hooks: - postprovision: - posix: - shell: sh - run: echo $'\n\nApp Service app has the following settings:\n' && echo "$WEB_APP_SETTINGS" | jq -r '.[]' | sed 's/\(.*\)/\t- \1/' && echo -e $"\nSee the settings in the portal:\033[1;36m $WEB_APP_CONFIG" - interactive: true - continueOnError: true - windows: - shell: pwsh - run: Write-Host "`n`nApp Service app has the following settings:`n" $WEB_APP_SETTINGS | ConvertFrom-Json | ForEach-Object { Write-Host "\t- $_" } - interactive: true - continueOnError: true - postdeploy: - posix: - shell: sh - run: echo -e $"\n\nOpen SSH session to App Service container at:\033[1;36m $WEB_APP_SSH\033[0m" && echo -e $"Stream App Service logs at:\033[1;36m $WEB_APP_LOG_STREAM" - interactive: true - continueOnError: true - windows: - shell: pwsh - run: Write-Host "`n`nOpen SSH session to App Service container at:`n" $WEB_APP_SSH; Write-Host "Stream App Service logs at:`n" $WEB_APP_LOG_STREAM - interactive: true - continueOnError: true \ No newline at end of file diff --git a/azureproject/production.py b/azureproject/production.py index dc47d25e..8a4c962f 100644 --- a/azureproject/production.py +++ b/azureproject/production.py @@ -2,12 +2,12 @@ # Configure Postgres database based on connection string of the libpq Keyword/Value form # https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING -conn_str = os.environ['AZURE_POSTGRESQL_CONNECTIONSTRING'] -conn_str_params = {pair.split('=')[0]: pair.split('=')[1] for pair in conn_str.split(' ')} - -DATABASE_URI = 'postgresql+psycopg2://{dbuser}:{dbpass}@{dbhost}/{dbname}'.format( - dbuser=conn_str_params['user'], - dbpass=conn_str_params['password'], - dbhost=conn_str_params['host'], - dbname=conn_str_params['dbname'] -) \ No newline at end of file +# Uncomment the following lines for App Service +# conn_str = os.environ['AZURE_POSTGRESQL_CONNECTIONSTRING'] +# conn_str_params = {pair.split('=')[0]: pair.split('=')[1] for pair in conn_str.split(' ')} +# DATABASE_URI = 'postgresql+psycopg2://{dbuser}:{dbpass}@{dbhost}/{dbname}'.format( +# dbuser=conn_str_params['user'], +# dbpass=conn_str_params['password'], +# dbhost=conn_str_params['host'], +# dbname=conn_str_params['dbname'] +# ) \ No newline at end of file diff --git a/infra/appinsights.bicep b/infra/appinsights.bicep deleted file mode 100644 index 83cfe17a..00000000 --- a/infra/appinsights.bicep +++ /dev/null @@ -1,1244 +0,0 @@ -param prefix string -param location string -param tags object -param workspaceId string - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { - name: '${prefix}-appinsights' - location: location - tags: tags - kind: 'web' - properties: { - Application_Type: 'web' - WorkspaceResourceId: workspaceId - } -} - -// 2020-09-01-preview because that is the latest valid version -resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { - name: '${prefix}-dashboard' - location: location - tags: tags - properties: { - lenses: [ - { - order: 0 - parts: [ - { - position: { - x: 0 - y: 0 - colSpan: 2 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'id' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' - asset: { - idInputName: 'id' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'overview' - } - } - { - position: { - x: 2 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'ProactiveDetection' - } - } - { - position: { - x: 3 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 4 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-04T01:20:33.345Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 5 - y: 0 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-08T18:47:35.237Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'ConfigurationId' - value: '78ce933e-e864-4b05-a27b-71fd55a6afad' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 0 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Usage' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 3 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - endTime: null - createdTime: '2018-05-04T01:22:35.782Z' - isInitialTime: true - grain: 1 - useDashboardTimeRange: false - } - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - } - } - { - position: { - x: 4 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Reliability' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 7 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'DataModel' - value: { - version: '1.0.0' - timeContext: { - durationMs: 86400000 - createdTime: '2018-05-04T23:42:40.072Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - isOptional: true - } - { - name: 'ConfigurationId' - value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' - isAdapter: true - asset: { - idInputName: 'ResourceId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'failures' - } - } - { - position: { - x: 8 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Responsiveness\r\n' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 11 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ResourceId' - value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - { - name: 'DataModel' - value: { - version: '1.0.0' - timeContext: { - durationMs: 86400000 - createdTime: '2018-05-04T23:43:37.804Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - isOptional: true - } - { - name: 'ConfigurationId' - value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' - isAdapter: true - asset: { - idInputName: 'ResourceId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'performance' - } - } - { - position: { - x: 12 - y: 1 - colSpan: 3 - rowSpan: 1 - } - metadata: { - inputs: [] - type: 'Extension/HubsExtension/PartType/MarkdownPart' - settings: { - content: { - settings: { - content: '# Browser' - title: '' - subtitle: '' - } - } - } - } - } - { - position: { - x: 15 - y: 1 - colSpan: 1 - rowSpan: 1 - } - metadata: { - inputs: [ - { - name: 'ComponentId' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'MetricsExplorerJsonDefinitionId' - value: 'BrowserPerformanceTimelineMetrics' - } - { - name: 'TimeContext' - value: { - durationMs: 86400000 - createdTime: '2018-05-08T12:16:27.534Z' - isInitialTime: false - grain: 1 - useDashboardTimeRange: false - } - } - { - name: 'CurrentFilter' - value: { - eventTypes: [ - 4 - 1 - 3 - 5 - 2 - 6 - 13 - ] - typeFacets: {} - isPermissive: false - } - } - { - name: 'id' - value: { - Name: applicationInsights.name - SubscriptionId: subscription().subscriptionId - ResourceGroup: resourceGroup().name - } - } - { - name: 'Version' - value: '1.0' - } - ] - #disable-next-line BCP036 - type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' - asset: { - idInputName: 'ComponentId' - type: 'ApplicationInsights' - } - defaultMenuItemId: 'browser' - } - } - { - position: { - x: 0 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'sessions/count' - aggregationType: 5 - namespace: 'microsoft.insights/components/kusto' - metricVisualization: { - displayName: 'Sessions' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'users/count' - aggregationType: 5 - namespace: 'microsoft.insights/components/kusto' - metricVisualization: { - displayName: 'Users' - color: '#7E58FF' - } - } - ] - title: 'Unique sessions and users' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'segmentationUsers' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'requests/failed' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Failed requests' - color: '#EC008C' - } - } - ] - title: 'Failed requests' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'failures' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'requests/duration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Server response time' - color: '#00BCF2' - } - } - ] - title: 'Server response time' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'performance' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 12 - y: 2 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/networkDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Page load network connect time' - color: '#7E58FF' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/processingDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Client processing time' - color: '#44F1C8' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/sendDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Send request time' - color: '#EB9371' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'browserTimings/receiveDuration' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Receiving response time' - color: '#0672F1' - } - } - ] - title: 'Average page load time breakdown' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 0 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'availabilityResults/availabilityPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Availability' - color: '#47BDF5' - } - } - ] - title: 'Average availability' - visualization: { - chartType: 3 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - openBladeOnClick: { - openBlade: true - destinationBlade: { - extensionName: 'HubsExtension' - bladeName: 'ResourceMenuBlade' - parameters: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - menuid: 'availability' - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'exceptions/server' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Server exceptions' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'dependencies/failed' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Dependency failures' - color: '#7E58FF' - } - } - ] - title: 'Server exceptions and Dependency failures' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processorCpuPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Processor time' - color: '#47BDF5' - } - } - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processCpuPercentage' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Process CPU' - color: '#7E58FF' - } - } - ] - title: 'Average processor and process CPU utilization' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 12 - y: 5 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'exceptions/browser' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Browser exceptions' - color: '#47BDF5' - } - } - ] - title: 'Browser exceptions' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 0 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'availabilityResults/count' - aggregationType: 7 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Availability test results count' - color: '#47BDF5' - } - } - ] - title: 'Availability test results count' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 4 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/processIOBytesPerSecond' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Process IO rate' - color: '#47BDF5' - } - } - ] - title: 'Average process I/O rate' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - { - position: { - x: 8 - y: 8 - colSpan: 4 - rowSpan: 3 - } - metadata: { - inputs: [ - { - name: 'options' - value: { - chart: { - metrics: [ - { - resourceMetadata: { - id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' - } - name: 'performanceCounters/memoryAvailableBytes' - aggregationType: 4 - namespace: 'microsoft.insights/components' - metricVisualization: { - displayName: 'Available memory' - color: '#47BDF5' - } - } - ] - title: 'Average available memory' - visualization: { - chartType: 2 - legendVisualization: { - isVisible: true - position: 2 - hideSubtitle: false - } - axisVisualization: { - x: { - isVisible: true - axisType: 2 - } - y: { - isVisible: true - axisType: 1 - } - } - } - } - } - } - { - name: 'sharedTimeRange' - isOptional: true - } - ] - #disable-next-line BCP036 - type: 'Extension/HubsExtension/PartType/MonitorChartPart' - settings: {} - } - } - ] - } - ] - } -} - -output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsights.properties.ConnectionString diff --git a/infra/main.bicep b/infra/main.bicep deleted file mode 100644 index 52b60ff3..00000000 --- a/infra/main.bicep +++ /dev/null @@ -1,48 +0,0 @@ -targetScope = 'subscription' - -@minLength(1) -@maxLength(64) -@description('Name which is used to generate a short unique hash for each resource') -param name string - -@minLength(1) -@description('Primary location for all resources') -param location string - -@secure() -@description('PostGreSQL Server administrator password') -param databasePassword string - -@secure() -@description('Django SECRET_KEY for securing signed data') -param secretKey string - -var resourceToken = toLower(uniqueString(subscription().id, name, location)) -var tags = { 'azd-env-name': name } - -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { - name: '${name}-rg' - location: location - tags: tags -} - -module resources 'resources.bicep' = { - name: 'resources' - scope: resourceGroup - params: { - name: name - location: location - resourceToken: resourceToken - tags: tags - databasePassword: databasePassword - secretKey: secretKey - } -} - -output AZURE_LOCATION string = location -output APPLICATIONINSIGHTS_CONNECTION_STRING string = resources.outputs.APPLICATIONINSIGHTS_CONNECTION_STRING -output WEB_URI string = resources.outputs.WEB_URI -output WEB_APP_SETTINGS array = resources.outputs.WEB_APP_SETTINGS -output WEB_APP_LOG_STREAM string = resources.outputs.WEB_APP_LOG_STREAM -output WEB_APP_SSH string = resources.outputs.WEB_APP_SSH -output WEB_APP_CONFIG string = resources.outputs.WEB_APP_CONFIG \ No newline at end of file diff --git a/infra/main.parameters.json b/infra/main.parameters.json deleted file mode 100644 index 87a1b669..00000000 --- a/infra/main.parameters.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "name": { - "value": "${AZURE_ENV_NAME}" - }, - "location": { - "value": "${AZURE_LOCATION}" - }, - "databasePassword": { - "value": "$(secretOrRandomPassword)" - }, - "secretKey": { - "value": "$(secretOrRandomPassword)" - } - } - } diff --git a/infra/resources.bicep b/infra/resources.bicep deleted file mode 100644 index 997d8921..00000000 --- a/infra/resources.bicep +++ /dev/null @@ -1,283 +0,0 @@ -param name string -param location string -param resourceToken string -param tags object -@secure() -param databasePassword string -@secure() -param secretKey string - -var prefix = '${name}-${resourceToken}' - -var pgServerName = '${prefix}-postgres-server' -var databaseSubnetName = 'database-subnet' -var webappSubnetName = 'webapp-subnet' - -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = { - name: '${prefix}-vnet' - location: location - tags: tags - properties: { - addressSpace: { - addressPrefixes: [ - '10.0.0.0/16' - ] - } - subnets: [ - { - name: databaseSubnetName - properties: { - addressPrefix: '10.0.0.0/24' - delegations: [ - { - name: '${prefix}-subnet-delegation' - properties: { - serviceName: 'Microsoft.DBforPostgreSQL/flexibleServers' - } - } - ] - } - } - { - name: webappSubnetName - properties: { - addressPrefix: '10.0.1.0/24' - delegations: [ - { - name: '${prefix}-subnet-delegation-web' - properties: { - serviceName: 'Microsoft.Web/serverFarms' - } - } - ] - } - } - ] - } - resource databaseSubnet 'subnets' existing = { - name: databaseSubnetName - } - resource webappSubnet 'subnets' existing = { - name: webappSubnetName - } -} - -resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { - name: '${pgServerName}.private.postgres.database.azure.com' - location: 'global' - tags: tags - dependsOn: [ - virtualNetwork - ] -} - -resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { - parent: privateDnsZone - name: '${pgServerName}-link' - location: 'global' - properties: { - registrationEnabled: false - virtualNetwork: { - id: virtualNetwork.id - } - } -} - -resource web 'Microsoft.Web/sites@2022-03-01' = { - name: '${prefix}-app-service' - location: location - tags: union(tags, { 'azd-service-name': 'web' }) - kind: 'app,linux' - properties: { - serverFarmId: appServicePlan.id - siteConfig: { - alwaysOn: true - linuxFxVersion: 'PYTHON|3.10' - ftpsState: 'Disabled' - appCommandLine: 'startup.sh' - } - httpsOnly: true - } - identity: { - type: 'SystemAssigned' - } - - resource appSettings 'config' = { - name: 'appsettings' - properties: { - SCM_DO_BUILD_DURING_DEPLOYMENT: 'true' - AZURE_POSTGRESQL_CONNECTIONSTRING: 'dbname=${pythonAppDatabase.name} host=${postgresServer.name}.postgres.database.azure.com port=5432 sslmode=require user=${postgresServer.properties.administratorLogin} password=${databasePassword}' - SECRET_KEY: secretKey - FLASK_DEBUG: 'False' - } - } - - resource logs 'config' = { - name: 'logs' - properties: { - applicationLogs: { - fileSystem: { - level: 'Verbose' - } - } - detailedErrorMessages: { - enabled: true - } - failedRequestsTracing: { - enabled: true - } - httpLogs: { - fileSystem: { - enabled: true - retentionInDays: 1 - retentionInMb: 35 - } - } - } - } - - resource webappVnetConfig 'networkConfig' = { - name: 'virtualNetwork' - properties: { - subnetResourceId: virtualNetwork::webappSubnet.id - } - } - - dependsOn: [ virtualNetwork ] - -} - -resource webdiagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { - name: 'AllLogs' - scope: web - properties: { - workspaceId: logAnalyticsWorkspace.id - logs: [ - { - category: 'AppServiceHTTPLogs' - enabled: true - } - { - category: 'AppServiceConsoleLogs' - enabled: true - } - { - category: 'AppServiceAppLogs' - enabled: true - } - { - category: 'AppServiceAuditLogs' - enabled: true - } - { - category: 'AppServiceIPSecAuditLogs' - enabled: true - } - { - category: 'AppServicePlatformLogs' - enabled: true - } - ] - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -resource appServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = { - name: '${prefix}-service-plan' - location: location - tags: tags - sku: { - name: 'B1' - } - properties: { - reserved: true - } -} - -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2020-03-01-preview' = { - name: '${prefix}-workspace' - location: location - tags: tags - properties: any({ - retentionInDays: 30 - features: { - searchVersion: 1 - } - sku: { - name: 'PerGB2018' - } - }) -} - -module applicationInsightsResources 'appinsights.bicep' = { - name: 'applicationinsights-resources' - params: { - prefix: prefix - location: location - tags: tags - workspaceId: logAnalyticsWorkspace.id - } -} - -resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-01-20-preview' = { - location: location - tags: tags - name: pgServerName - sku: { - name: 'Standard_B1ms' - tier: 'Burstable' - } - properties: { - version: '12' - administratorLogin: 'postgresadmin' - administratorLoginPassword: databasePassword - storage: { - storageSizeGB: 128 - } - backup: { - backupRetentionDays: 7 - geoRedundantBackup: 'Disabled' - } - network: { - delegatedSubnetResourceId: virtualNetwork::databaseSubnet.id - privateDnsZoneArmResourceId: privateDnsZone.id - } - highAvailability: { - mode: 'Disabled' - } - maintenanceWindow: { - customWindow: 'Disabled' - dayOfWeek: 0 - startHour: 0 - startMinute: 0 - } - } - - dependsOn: [ - privateDnsZoneLink - ] -} - -resource pythonAppDatabase 'Microsoft.DBforPostgreSQL/flexibleServers/databases@2022-01-20-preview' = { - parent: postgresServer - name: 'pythonapp' -} - -output WEB_URI string = 'https://${web.properties.defaultHostName}' -output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsightsResources.outputs.APPLICATIONINSIGHTS_CONNECTION_STRING - -resource webAppSettings 'Microsoft.Web/sites/config@2022-03-01' existing = { - name: web::appSettings.name - parent: web -} - -var webAppSettingsKeys = map(items(webAppSettings.list().properties), setting => setting.key) -output WEB_APP_SETTINGS array = webAppSettingsKeys -output WEB_APP_LOG_STREAM string = format('https://portal.azure.com/#@/resource{0}/logStream', web.id) -output WEB_APP_SSH string = format('https://{0}.scm.azurewebsites.net/webssh/host', web.name) -output WEB_APP_CONFIG string = format('https://portal.azure.com/#@/resource{0}/configuration', web.id) \ No newline at end of file diff --git a/startup.sh b/startup.sh deleted file mode 100644 index 398a88cc..00000000 --- a/startup.sh +++ /dev/null @@ -1,4 +0,0 @@ -flask db upgrade -gunicorn --workers 2 --threads 4 --timeout 60 --access-logfile \ - '-' --error-logfile '-' --bind=0.0.0.0:8000 \ - --chdir=/home/site/wwwroot app:app From fb079858067fc89489cb2e2c82c766f92d1f6307 Mon Sep 17 00:00:00 2001 From: Cephas Lin Date: Tue, 28 Nov 2023 15:59:38 +0100 Subject: [PATCH 02/11] sync with main --- .devcontainer/README.md | 11 +++-- .devcontainer/devcontainer.json | 8 ++-- .devcontainer/docker-compose.yml | 4 ++ .env.sample | 4 +- .env.sample.devcontainer | 2 +- .github/workflows/python-test.yaml | 2 +- .gitignore | 2 +- README.md | 72 ++++++++---------------------- 8 files changed, 39 insertions(+), 66 deletions(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index b8a8094e..e98f6a87 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,5 +1,10 @@ -# Dev container +# .devcontainer directory -This `.devcontainer` directory contains the configuration for a [dev container](https://docs.github.com/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers). It lets you open the repository in a [GitHub codespace](https://docs.github.com/codespaces/overview). +This `.devcontainer` directory contains the configuration for a [dev container](https://docs.github.com/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers) and isn't used by the sample application. -The dev container is configured to have the [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/overview), so you can run `azd` commands directly. +The dev container configuration lets you open the repository in a [GitHub codespace](https://docs.github.com/codespaces/overview) or a dev container in Visual Studio Code. For your convenience, the dev container is configured with the following: + +- Python +- PostgreSQL +- Redis +- [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/overview) (so you can run `azd` commands directly). diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 31630779..c8d7d81c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,11 +1,11 @@ { - "name": "msdocs-flask-postgresql-sample-app", + "name": "python-app-service-postgresql-redis-infra", "dockerComposeFile": "docker-compose.yml", "service": "app", "workspaceFolder": "/workspace", "features": { "ghcr.io/azure/azure-dev/azd:latest": {} - }, + }, "customizations": { "vscode": { // Add the IDs of extensions you want installed when the container is created. @@ -51,9 +51,9 @@ } } }, - // Use 'forwardPorts' to make a list of ports inside the container available locally. + // Use 'forwardPorts' to make a list of ports inside the container available locally. 5000 is for Flask, 8000 is for Django, and 5432 is for PostgreSQL. "forwardPorts": [ - 5000, 5432 + 8000, 5000, 5432 ], // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "", diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index c6aa90a3..21855366 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -28,5 +28,9 @@ services: # Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally. # (Adding the "ports" property to this file will not forward from a Codespace.) + redis: + image: redis + restart: unless-stopped + volumes: postgres-data: diff --git a/.env.sample b/.env.sample index ccd36937..9ee63d95 100644 --- a/.env.sample +++ b/.env.sample @@ -1,6 +1,6 @@ FLASK_DEBUG=True -DBNAME= +DBNAME= DBHOST= DBUSER= DBPASS= -SECRET_KEY= \ No newline at end of file +CACHELOCATION= diff --git a/.env.sample.devcontainer b/.env.sample.devcontainer index 96b0544b..1314b887 100644 --- a/.env.sample.devcontainer +++ b/.env.sample.devcontainer @@ -3,4 +3,4 @@ DBNAME=app DBHOST=localhost DBUSER=app_user DBPASS=app_password -SECRET_KEY=flask-insecure-secret-key +CACHELOCATION=redis://redis:6379/0 diff --git a/.github/workflows/python-test.yaml b/.github/workflows/python-test.yaml index 72957267..a69359d7 100644 --- a/.github/workflows/python-test.yaml +++ b/.github/workflows/python-test.yaml @@ -17,7 +17,7 @@ jobs: python_version: ["3.8", "3.9", "3.10", "3.11"] services: postgres: - image: postgres:11 + image: postgres:12 env: POSTGRES_PASSWORD: postgres ports: diff --git a/.gitignore b/.gitignore index 47ff4dfc..9b957b1b 100644 --- a/.gitignore +++ b/.gitignore @@ -128,4 +128,4 @@ dmypy.json # Pyre type checker .pyre/ -.azure +.azure \ No newline at end of file diff --git a/README.md b/README.md index 65440d6d..2ef58ffc 100644 --- a/README.md +++ b/README.md @@ -16,64 +16,47 @@ The [requirements.txt](./requirements.txt) has the following packages, all used | [python-dotenv](https://pypi.org/project/python-dotenv/) | Read key-value pairs from .env file and set them as environment variables. In this sample app, those variables describe how to connect to the database locally.

Flask's [dotenv support](https://flask.palletsprojects.com/en/2.1.x/cli/#environment-variables-from-dotenv) sets environment variables automatically from an `.env` file. | | [flask_wtf](https://pypi.org/project/Flask-WTF/) | Form rendering, validation, and CSRF protection for Flask with WTForms. Uses CSRFProtect extension. | -## Using this project with the Azure Developer CLI (azd) +## Run the sample -This project is designed to work well with the [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/overview), -which makes it easier to develop apps locally, deploy them to Azure, and monitor them. +This project has a [dev container configuration](.devcontainer/), which makes it easier to develop apps locally, deploy them to Azure, and monitor them. The easiest way to run this sample application is inside a GitHub codespace. Follow these steps: -### Local development +1. Fork this repository to your account. For instructions, see [Fork a repo](https://docs.github.com/get-started/quickstart/fork-a-repo). -This project has Dev Container support, so you can open it in Github Codespaces or local VS Code with the Dev Containers extension. +1. From the repository root of your fork, select **Code** > **Codespaces** > **+**. -🎥 [Watch a screencast of running the app in Github Codespaces.](https://www.youtube.com/watch?v=r6Hnp9RXUpY) - -Steps for running the server: - -1. (Optional) If you're unable to open the Dev Container, [create a Python virtual environment](https://docs.python.org/3/tutorial/venv.html#creating-virtual-environments) and activate the virtual environment. - -2. Install the requirements: +1. In the codespace terminal, run the following commands: ```shell + # Install requirements python3 -m pip install -r requirements.txt - ``` - -3. Create an `.env` file using `.env.sample` as a guide. Set the value of `DBNAME` to the name of an existing database in your local PostgreSQL instance. Set the values of `DBHOST`, `DBUSER`, and `DBPASS` as appropriate for your local PostgreSQL instance. If you're in the Dev Container, copy the values from `.env.sample.devcontainer`. - -4. In the `.env` file, fill in a secret value for `SECRET_KEY`. You can use this command to generate an appropriate value: - - ```shell - python -c 'import secrets; print(secrets.token_hex())' - ``` - -5. Run the migrations: - - ```shell + # Create .env with environment variables + cp .env.sample.devcontainer .env + # Run database migrations python3 -m flask db upgrade - ``` - -6. Run the local server: (or use VS Code "Run" button and select "Run server") - - ```shell + # Start the development server python3 -m flask run ``` -### Deployment +1. When you see the message `Your application running on port 8000 is available.`, click **Open in Browser**. + +### Quick deploy -This repo is set up for deployment on Azure App Service (w/PostGreSQL server) using the configuration files in the `infra` folder. +This project is designed to work well with the [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/overview), which makes it easier to develop apps locally, deploy them to Azure, and monitor them. -🎥 [Watch a screencast of deploying and re-deploying the app.](https://www.youtube.com/watch?v=r6Hnp9RXUpY) +🎥 Watch a deployment of the code in [this screencast](https://www +.youtube.com/watch?v=JDlZ4TgPKYc). Steps for deployment: 1. Sign up for a [free Azure account](https://azure.microsoft.com/free/) -2. Install the [Azure Dev CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd). (If you opened this repository in a Dev Container, that part will be done for you.) +2. Install the [Azure Dev CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd). (If you opened this repository in a Dev Container, it's already installed for you.) 3. Initialize a new `azd` environment: ```shell azd init ``` - It will prompt you to provide a name (like "flask-app") that will later be used in the name of the deployed resources. + It will prompt you to provide a name (like "flask-app"), which will later be used in the name of the deployed resources. 4. Provision and deploy all the resources: @@ -93,25 +76,6 @@ Steps for deployment: azd deploy ``` -### CI/CD pipeline - -This project includes a Github workflow for deploying the resources to Azure -on every push. That workflow requires several Azure-related authentication secrets to be stored as Github action secrets. To set that up, run: - -```shell -azd pipeline config -``` - -### Monitoring - -The deployed resources include a Log Analytics workspace with an Application Insights dashboard to measure metrics like server response time. - -To open that dashboard, just run: - -```shell -azd monitor --overview -``` - ## Getting help If you're working with this project and running into issues, please post in [Issues](/issues). From e96741566da95e9441f8ee665ab224dda0fb51f9 Mon Sep 17 00:00:00 2001 From: Cephas Lin Date: Mon, 20 Jan 2025 16:10:33 +0000 Subject: [PATCH 03/11] add copilot ext --- .devcontainer/devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 452cd1ef..881c06aa 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -14,7 +14,8 @@ "ms-python.python", "ms-python.vscode-pylance", "mtxr.sqltools", - "mtxr.sqltools-driver-pg" + "mtxr.sqltools-driver-pg", + "GitHub.copilot" ], "settings": { "sqltools.connections": [ From bde4846e86a4c365a2a0b3aea259bc342dab9bdc Mon Sep 17 00:00:00 2001 From: krivamar <85482111+krivamar@users.noreply.github.com> Date: Thu, 3 Apr 2025 20:26:26 +0200 Subject: [PATCH 04/11] Add or update the Azure App Service build and deployment workflow config --- .../starter-no-infra_web3-hotel-krivamar.yml | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 .github/workflows/starter-no-infra_web3-hotel-krivamar.yml diff --git a/.github/workflows/starter-no-infra_web3-hotel-krivamar.yml b/.github/workflows/starter-no-infra_web3-hotel-krivamar.yml new file mode 100644 index 00000000..7414debf --- /dev/null +++ b/.github/workflows/starter-no-infra_web3-hotel-krivamar.yml @@ -0,0 +1,81 @@ +# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy +# More GitHub Actions for Azure: https://github.com/Azure/actions +# More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions + +name: Build and deploy Python app to Azure Web App - web3-hotel-krivamar + +on: + push: + branches: + - starter-no-infra + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read #This is required for actions/checkout + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python version + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Create and start virtual environment + run: | + python -m venv venv + source venv/bin/activate + + - name: Install dependencies + run: pip install -r requirements.txt + + # Optional: Add step to run tests here (PyTest, Django test suites, etc.) + + - name: Zip artifact for deployment + run: zip release.zip ./* -r + + - name: Upload artifact for deployment jobs + uses: actions/upload-artifact@v4 + with: + name: python-app + path: | + release.zip + !venv/ + + deploy: + runs-on: ubuntu-latest + needs: build + environment: + name: 'Production' + url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} + permissions: + id-token: write #This is required for requesting the JWT + contents: read #This is required for actions/checkout + + steps: + - name: Download artifact from build job + uses: actions/download-artifact@v4 + with: + name: python-app + + - name: Unzip artifact for deployment + run: unzip release.zip + + + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_70BA313A5A574BE48907CF1746247471 }} + tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_FF2272F627484226B7251500C1AE4F28 }} + subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_1ABA9C051AE349F1B1B572F910695AA2 }} + + - name: 'Deploy to Azure Web App' + uses: azure/webapps-deploy@v3 + id: deploy-to-webapp + with: + app-name: 'web3-hotel-krivamar' + slot-name: 'Production' + \ No newline at end of file From 3c4da9d15c0a8eddf08a7eb9a4a8798b408402a0 Mon Sep 17 00:00:00 2001 From: krivamar <85482111+krivamar@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:37:14 +0000 Subject: [PATCH 05/11] Configure Azure database connecton --- azureproject/production.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/azureproject/production.py b/azureproject/production.py index 331bd036..4ba68a96 100644 --- a/azureproject/production.py +++ b/azureproject/production.py @@ -1,8 +1,8 @@ import os -# DATABASE_URI = 'postgresql+psycopg2://{dbuser}:{dbpass}@{dbhost}/{dbname}'.format( -# dbuser=os.getenv('AZURE_POSTGRESQL_USER'), -# dbpass=os.getenv('AZURE_POSTGRESQL_PASSWORD'), -# dbhost=os.getenv('AZURE_POSTGRESQL_HOST'), -# dbname=os.getenv('AZURE_POSTGRESQL_NAME') -# ) + DATABASE_URI = 'postgresql+psycopg2://{dbuser}:{dbpass}@{dbhost}/{dbname}'.format( + dbuser=os.getenv('AZURE_POSTGRESQL_USER'), + dbpass=os.getenv('AZURE_POSTGRESQL_PASSWORD'), + dbhost=os.getenv('AZURE_POSTGRESQL_HOST'), + dbname=os.getenv('AZURE_POSTGRESQL_NAME') + ) From c9e21fdfaa4aeec5da6b9c0de239955e1e8e6f9d Mon Sep 17 00:00:00 2001 From: krivamar <85482111+krivamar@users.noreply.github.com> Date: Thu, 3 Apr 2025 19:43:38 +0000 Subject: [PATCH 06/11] add port 8000 --- app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 8fc6a89b..f187c73b 100644 --- a/app.py +++ b/app.py @@ -118,6 +118,6 @@ def star_rating(id): def favicon(): return send_from_directory(os.path.join(app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon') - +# add port 8000 if __name__ == '__main__': - app.run() + app.run(host='0.0.0.0', port=8000) From 2b0267c017af1e55f6954386404114365ee35afd Mon Sep 17 00:00:00 2001 From: krivamar <85482111+krivamar@users.noreply.github.com> Date: Thu, 3 Apr 2025 20:04:41 +0000 Subject: [PATCH 07/11] psycopg2-binary requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b7a30b25..fa9acc63 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,6 @@ Flask Flask-Migrate Flask-SQLAlchemy flask_wtf -psycopg2 +psycopg2-binary python-dotenv SQLAlchemy \ No newline at end of file From ebe7ffe9c13b348e99f916b604e5ceab71984f89 Mon Sep 17 00:00:00 2001 From: krivamar <85482111+krivamar@users.noreply.github.com> Date: Tue, 8 Apr 2025 20:44:22 +0200 Subject: [PATCH 08/11] Add or update the Azure App Service build and deployment workflow config --- .github/workflows/starter-no-infra_web3-hotel-krivamar.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/starter-no-infra_web3-hotel-krivamar.yml b/.github/workflows/starter-no-infra_web3-hotel-krivamar.yml index 7414debf..0a048cea 100644 --- a/.github/workflows/starter-no-infra_web3-hotel-krivamar.yml +++ b/.github/workflows/starter-no-infra_web3-hotel-krivamar.yml @@ -68,9 +68,9 @@ jobs: - name: Login to Azure uses: azure/login@v2 with: - client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_70BA313A5A574BE48907CF1746247471 }} - tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_FF2272F627484226B7251500C1AE4F28 }} - subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_1ABA9C051AE349F1B1B572F910695AA2 }} + client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_EB9DA3824EFE47CD85819EB7185C6CEE }} + tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_585414977F2D4BC3A05AE4A7A1384849 }} + subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_BF0EEB52860944129C22EDD6B5D8E130 }} - name: 'Deploy to Azure Web App' uses: azure/webapps-deploy@v3 From 6e858ff033331640cfd4135b6beb186273c2c207 Mon Sep 17 00:00:00 2001 From: krivamar <85482111+krivamar@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:37:07 +0000 Subject: [PATCH 09/11] Fix formatting by adding a newline at the end of the production.py file --- azureproject/production.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azureproject/production.py b/azureproject/production.py index 4ba68a96..cf67e72c 100644 --- a/azureproject/production.py +++ b/azureproject/production.py @@ -5,4 +5,4 @@ dbpass=os.getenv('AZURE_POSTGRESQL_PASSWORD'), dbhost=os.getenv('AZURE_POSTGRESQL_HOST'), dbname=os.getenv('AZURE_POSTGRESQL_NAME') - ) + ) \ No newline at end of file From 96d97b3efbb0db2cf38ac41650e9457824724970 Mon Sep 17 00:00:00 2001 From: krivamar <85482111+krivamar@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:37:53 +0000 Subject: [PATCH 10/11] test id --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fa9acc63..b7a30b25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,6 @@ Flask Flask-Migrate Flask-SQLAlchemy flask_wtf -psycopg2-binary +psycopg2 python-dotenv SQLAlchemy \ No newline at end of file From fa080a7f39b13f419ad588e6473553f16f7bf857 Mon Sep 17 00:00:00 2001 From: krivamar <85482111+krivamar@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:41:22 +0000 Subject: [PATCH 11/11] #port 8000 --- app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index f187c73b..bf07d074 100644 --- a/app.py +++ b/app.py @@ -119,5 +119,5 @@ def favicon(): return send_from_directory(os.path.join(app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon') # add port 8000 -if __name__ == '__main__': - app.run(host='0.0.0.0', port=8000) +#if __name__ == '__main__': +# app.run(host='0.0.0.0', port=8000)