From 02562666427ae69e262f1d6e14dbfc96d1e2e1a3 Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Tue, 8 Oct 2024 15:57:37 +0200 Subject: [PATCH 01/11] feat(terraform): azure function and service plan --- terraform/application_insights.tf | 7 +++++++ terraform/function.tf | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 terraform/application_insights.tf create mode 100644 terraform/function.tf diff --git a/terraform/application_insights.tf b/terraform/application_insights.tf new file mode 100644 index 0000000..e3e9bd4 --- /dev/null +++ b/terraform/application_insights.tf @@ -0,0 +1,7 @@ +# (Optional) Application Insights - For Monitoring +resource "azurerm_application_insights" "apin" { + name = "dp-appinsights" + location = azurerm_resource_group.data_platform.location + resource_group_name = azurerm_resource_group.data_platform.name + application_type = "web" +} diff --git a/terraform/function.tf b/terraform/function.tf new file mode 100644 index 0000000..cff76bd --- /dev/null +++ b/terraform/function.tf @@ -0,0 +1,24 @@ +# App Service Plan +resource "azurerm_service_plan" "func" { + name = "dp-func-appserviceplan" + location = azurerm_resource_group.data_platform.location + resource_group_name = azurerm_resource_group.data_platform.name + os_type = "Linux" + sku_name = "B1" +} + +# Function App +resource "azurerm_linux_function_app" "func" { + name = "dev-aquaplatform-func" + location = azurerm_resource_group.data_platform.location + resource_group_name = azurerm_resource_group.data_platform.name + storage_account_name = azurerm_storage_account.data_lake.name + storage_account_access_key = azurerm_storage_account.data_lake.primary_access_key + service_plan_id = azurerm_service_plan.func.id + site_config { + application_insights_connection_string = azurerm_application_insights.apin.connection_string + application_stack { + dotnet_version = "8.0" + } + } +} From 8ac0f85ab2fa20db927ff2682ff93f4608af5b8c Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Tue, 8 Oct 2024 15:58:49 +0200 Subject: [PATCH 02/11] refactor: remove comment --- terraform/application_insights.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/terraform/application_insights.tf b/terraform/application_insights.tf index e3e9bd4..2917027 100644 --- a/terraform/application_insights.tf +++ b/terraform/application_insights.tf @@ -1,4 +1,3 @@ -# (Optional) Application Insights - For Monitoring resource "azurerm_application_insights" "apin" { name = "dp-appinsights" location = azurerm_resource_group.data_platform.location From 014ee2ffaaeed49667c7c9e6cbba95f37656554a Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Wed, 9 Oct 2024 13:23:46 +0200 Subject: [PATCH 03/11] refactor: rename api folder to src --- {api => src}/AquaApi/AquaApi.csproj | 0 {api => src}/AquaApi/Dockerfile | 0 {api => src}/AquaApi/MeasurementRow.cs | 0 {api => src}/AquaApi/Program.cs | 0 {api => src}/AquaApi/Properties/launchSettings.json | 0 {api => src}/AquaApi/appsettings.json | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {api => src}/AquaApi/AquaApi.csproj (100%) rename {api => src}/AquaApi/Dockerfile (100%) rename {api => src}/AquaApi/MeasurementRow.cs (100%) rename {api => src}/AquaApi/Program.cs (100%) rename {api => src}/AquaApi/Properties/launchSettings.json (100%) rename {api => src}/AquaApi/appsettings.json (100%) diff --git a/api/AquaApi/AquaApi.csproj b/src/AquaApi/AquaApi.csproj similarity index 100% rename from api/AquaApi/AquaApi.csproj rename to src/AquaApi/AquaApi.csproj diff --git a/api/AquaApi/Dockerfile b/src/AquaApi/Dockerfile similarity index 100% rename from api/AquaApi/Dockerfile rename to src/AquaApi/Dockerfile diff --git a/api/AquaApi/MeasurementRow.cs b/src/AquaApi/MeasurementRow.cs similarity index 100% rename from api/AquaApi/MeasurementRow.cs rename to src/AquaApi/MeasurementRow.cs diff --git a/api/AquaApi/Program.cs b/src/AquaApi/Program.cs similarity index 100% rename from api/AquaApi/Program.cs rename to src/AquaApi/Program.cs diff --git a/api/AquaApi/Properties/launchSettings.json b/src/AquaApi/Properties/launchSettings.json similarity index 100% rename from api/AquaApi/Properties/launchSettings.json rename to src/AquaApi/Properties/launchSettings.json diff --git a/api/AquaApi/appsettings.json b/src/AquaApi/appsettings.json similarity index 100% rename from api/AquaApi/appsettings.json rename to src/AquaApi/appsettings.json From 2ba7ac37bc60514cd3d8736e4f1c6e91ebcb17f4 Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Wed, 9 Oct 2024 13:24:40 +0200 Subject: [PATCH 04/11] refactor: rename api folder to src --- .github/workflows/publish-api-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-api-image.yaml b/.github/workflows/publish-api-image.yaml index 2842f58..23e9d8e 100644 --- a/.github/workflows/publish-api-image.yaml +++ b/.github/workflows/publish-api-image.yaml @@ -24,7 +24,7 @@ jobs: # Step 4: Build the Docker image - name: Build the Docker image - run: docker build -t "${{ secrets.ACR_SERVER }}/aquaapi:latest" ./api/AquaApi + run: docker build -t "${{ secrets.ACR_SERVER }}/aquaapi:latest" ./src/AquaApi # Step 5: Push the Docker image to Azure Container Registry - name: Push the Docker image From 9face1bf98413ab428105dff11051fa970527ba2 Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Wed, 9 Oct 2024 13:25:37 +0200 Subject: [PATCH 05/11] refactor: rename api folder to src in solution as well --- dataplatform-aquaculture-template.sln | 45 +++++++++++++-------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/dataplatform-aquaculture-template.sln b/dataplatform-aquaculture-template.sln index 5bf1763..a54da26 100644 --- a/dataplatform-aquaculture-template.sln +++ b/dataplatform-aquaculture-template.sln @@ -1,30 +1,29 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.002.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "api", "api", "{A6ED9EDF-D599-47B8-A6B2-2844E3FEBF9C}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A6ED9EDF-D599-47B8-A6B2-2844E3FEBF9C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AquaApi", "api\AquaApi\AquaApi.csproj", "{35950192-252F-44A8-B710-63E470A67118}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AquaApi", "src\AquaApi\AquaApi.csproj", "{35950192-252F-44A8-B710-63E470A67118}" EndProject Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {35950192-252F-44A8-B710-63E470A67118}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {35950192-252F-44A8-B710-63E470A67118}.Debug|Any CPU.Build.0 = Debug|Any CPU - {35950192-252F-44A8-B710-63E470A67118}.Release|Any CPU.ActiveCfg = Release|Any CPU - {35950192-252F-44A8-B710-63E470A67118}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {35950192-252F-44A8-B710-63E470A67118} = {A6ED9EDF-D599-47B8-A6B2-2844E3FEBF9C} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {00A64A12-39CC-40EA-996D-79EA6539A35B} - EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {35950192-252F-44A8-B710-63E470A67118}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35950192-252F-44A8-B710-63E470A67118}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35950192-252F-44A8-B710-63E470A67118}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35950192-252F-44A8-B710-63E470A67118}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {35950192-252F-44A8-B710-63E470A67118} = {A6ED9EDF-D599-47B8-A6B2-2844E3FEBF9C} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {00A64A12-39CC-40EA-996D-79EA6539A35B} + EndGlobalSection EndGlobal From 530940403f76a3ed737116fb21de2e6f5166f40b Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Wed, 9 Oct 2024 14:20:12 +0200 Subject: [PATCH 06/11] feat(function): add azure function that runs every second --- .vscode/extensions.json | 6 + .vscode/launch.json | 11 + .vscode/settings.json | 8 + .vscode/tasks.json | 81 ++++++ dataplatform-aquaculture-template.sln | 45 +-- src/DataGeneratorFunction/.gitignore | 264 ++++++++++++++++++ .../DataGeneratorFunction.cs | 27 ++ .../DataGeneratorFunction.csproj | 31 ++ src/DataGeneratorFunction/Program.cs | 15 + .../Properties/launchSettings.json | 9 + src/DataGeneratorFunction/host.json | 12 + src/DataGeneratorFunction/readme.md | 11 + 12 files changed, 499 insertions(+), 21 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 src/DataGeneratorFunction/.gitignore create mode 100644 src/DataGeneratorFunction/DataGeneratorFunction.cs create mode 100644 src/DataGeneratorFunction/DataGeneratorFunction.csproj create mode 100644 src/DataGeneratorFunction/Program.cs create mode 100644 src/DataGeneratorFunction/Properties/launchSettings.json create mode 100644 src/DataGeneratorFunction/host.json create mode 100644 src/DataGeneratorFunction/readme.md diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..bb76300 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-azuretools.vscode-azurefunctions", + "ms-dotnettools.csharp" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..894cbe6 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to .NET Functions", + "type": "coreclr", + "request": "attach", + "processId": "${command:azureFunctions.pickProcess}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..54b2105 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "azureFunctions.deploySubpath": "src/DataGeneratorFunction/bin/Release/net8.0/publish", + "azureFunctions.projectLanguage": "C#", + "azureFunctions.projectRuntime": "~4", + "debug.internalConsoleOptions": "neverOpen", + "azureFunctions.preDeployTask": "publish (functions)", + "azureFunctions.projectSubpath": "src/DataGeneratorFunction" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..7734ecb --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,81 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "clean (functions)", + "command": "dotnet", + "args": [ + "clean", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/src/DataGeneratorFunction" + } + }, + { + "label": "build (functions)", + "command": "dotnet", + "args": [ + "build", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "clean (functions)", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/src/DataGeneratorFunction" + } + }, + { + "label": "clean release (functions)", + "command": "dotnet", + "args": [ + "clean", + "--configuration", + "Release", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/src/DataGeneratorFunction" + } + }, + { + "label": "publish (functions)", + "command": "dotnet", + "args": [ + "publish", + "--configuration", + "Release", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "clean release (functions)", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/src/DataGeneratorFunction" + } + }, + { + "type": "func", + "dependsOn": "build (functions)", + "options": { + "cwd": "${workspaceFolder}/src/DataGeneratorFunction" + }, + "command": "host start", + "isBackground": true, + "problemMatcher": "$func-dotnet-watch" + } + ] +} \ No newline at end of file diff --git a/dataplatform-aquaculture-template.sln b/dataplatform-aquaculture-template.sln index a54da26..97a67d6 100644 --- a/dataplatform-aquaculture-template.sln +++ b/dataplatform-aquaculture-template.sln @@ -2,28 +2,31 @@ # Visual Studio Version 17 VisualStudioVersion = 17.5.002.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A6ED9EDF-D599-47B8-A6B2-2844E3FEBF9C}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AquaApi", "src\AquaApi\AquaApi.csproj", "{35950192-252F-44A8-B710-63E470A67118}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataGeneratorFunction", "src\DataGeneratorFunction\DataGeneratorFunction.csproj", "{3FFDA260-76E7-4E96-9F97-5B80E30FF2F2}" +EndProject Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {35950192-252F-44A8-B710-63E470A67118}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {35950192-252F-44A8-B710-63E470A67118}.Debug|Any CPU.Build.0 = Debug|Any CPU - {35950192-252F-44A8-B710-63E470A67118}.Release|Any CPU.ActiveCfg = Release|Any CPU - {35950192-252F-44A8-B710-63E470A67118}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {35950192-252F-44A8-B710-63E470A67118} = {A6ED9EDF-D599-47B8-A6B2-2844E3FEBF9C} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {00A64A12-39CC-40EA-996D-79EA6539A35B} - EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {35950192-252F-44A8-B710-63E470A67118}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35950192-252F-44A8-B710-63E470A67118}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35950192-252F-44A8-B710-63E470A67118}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35950192-252F-44A8-B710-63E470A67118}.Release|Any CPU.Build.0 = Release|Any CPU + {3FFDA260-76E7-4E96-9F97-5B80E30FF2F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FFDA260-76E7-4E96-9F97-5B80E30FF2F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FFDA260-76E7-4E96-9F97-5B80E30FF2F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FFDA260-76E7-4E96-9F97-5B80E30FF2F2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {00A64A12-39CC-40EA-996D-79EA6539A35B} + EndGlobalSection EndGlobal diff --git a/src/DataGeneratorFunction/.gitignore b/src/DataGeneratorFunction/.gitignore new file mode 100644 index 0000000..ff5b00c --- /dev/null +++ b/src/DataGeneratorFunction/.gitignore @@ -0,0 +1,264 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# Azure Functions localsettings file +local.settings.json + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc \ No newline at end of file diff --git a/src/DataGeneratorFunction/DataGeneratorFunction.cs b/src/DataGeneratorFunction/DataGeneratorFunction.cs new file mode 100644 index 0000000..01064d8 --- /dev/null +++ b/src/DataGeneratorFunction/DataGeneratorFunction.cs @@ -0,0 +1,27 @@ +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Logging; + +namespace DataGeneratorFunction +{ + public class DataGeneratorFunction + { + private readonly ILogger _logger; + + public DataGeneratorFunction(ILoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + } + + [Function("DataGeneratorFunction")] + public void Run([TimerTrigger("*/1 * * * * *")] TimerInfo myTimer) + { + Console.WriteLine("Running data generator function"); + _logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}"); + + if (myTimer.ScheduleStatus is not null) + { + _logger.LogInformation($"Next timer schedule at: {myTimer.ScheduleStatus.Next}"); + } + } + } +} diff --git a/src/DataGeneratorFunction/DataGeneratorFunction.csproj b/src/DataGeneratorFunction/DataGeneratorFunction.csproj new file mode 100644 index 0000000..d9e46fd --- /dev/null +++ b/src/DataGeneratorFunction/DataGeneratorFunction.csproj @@ -0,0 +1,31 @@ + + + net8.0 + v4 + Exe + enable + enable + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + Never + + + + + + \ No newline at end of file diff --git a/src/DataGeneratorFunction/Program.cs b/src/DataGeneratorFunction/Program.cs new file mode 100644 index 0000000..4e74c98 --- /dev/null +++ b/src/DataGeneratorFunction/Program.cs @@ -0,0 +1,15 @@ +using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; + +var host = new HostBuilder() + .ConfigureFunctionsWebApplication( + ) + .ConfigureServices(services => + { + services.AddApplicationInsightsTelemetryWorkerService(); // Optional: App Insights + services.ConfigureFunctionsApplicationInsights(); // Optional: App Insights config + }) + .Build(); + +host.Run(); \ No newline at end of file diff --git a/src/DataGeneratorFunction/Properties/launchSettings.json b/src/DataGeneratorFunction/Properties/launchSettings.json new file mode 100644 index 0000000..e194d35 --- /dev/null +++ b/src/DataGeneratorFunction/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "DataGeneratorFunction": { + "commandName": "Project", + "commandLineArgs": "--port 7259", + "launchBrowser": false + } + } +} \ No newline at end of file diff --git a/src/DataGeneratorFunction/host.json b/src/DataGeneratorFunction/host.json new file mode 100644 index 0000000..ee5cf5f --- /dev/null +++ b/src/DataGeneratorFunction/host.json @@ -0,0 +1,12 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + }, + "enableLiveMetricsFilters": true + } + } +} \ No newline at end of file diff --git a/src/DataGeneratorFunction/readme.md b/src/DataGeneratorFunction/readme.md new file mode 100644 index 0000000..0b247b5 --- /dev/null +++ b/src/DataGeneratorFunction/readme.md @@ -0,0 +1,11 @@ +# TimerTrigger - C# + +The `TimerTrigger` makes it incredibly easy to have your functions executed on a schedule. This sample demonstrates a simple use case of calling your function every 5 minutes. + +## How it works + +For a `TimerTrigger` to work, you provide a schedule in the form of a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression)(See the link for full details). A cron expression is a string with 6 separate expressions which represent a given schedule via patterns. The pattern we use to represent every 5 minutes is `0 */5 * * * *`. This, in plain text, means: "When seconds is equal to 0, minutes is divisible by 5, for any hour, day of the month, month, day of the week, or year". + +## Learn more + + Documentation \ No newline at end of file From d1bcbc665e56f3009c1877b4bdf41adf17572a2b Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Wed, 9 Oct 2024 14:41:09 +0200 Subject: [PATCH 07/11] ci(functions): add cicd scriptt to publish function --- .../publish-data-generation-function.yaml | 36 +++++++++++++++++++ src/DataGeneratorFunction/.gitignore | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/publish-data-generation-function.yaml diff --git a/.github/workflows/publish-data-generation-function.yaml b/.github/workflows/publish-data-generation-function.yaml new file mode 100644 index 0000000..ac6a412 --- /dev/null +++ b/.github/workflows/publish-data-generation-function.yaml @@ -0,0 +1,36 @@ +name: Deploy DotNet project to Azure Function App + +on: + [push] + +env: + AZURE_FUNCTIONAPP_NAME: 'dev-aquaplatform-func' # set this to your function app name on Azure + AZURE_FUNCTIONAPP_PACKAGE_PATH: 'src/DataGeneratorFunction' # set this to the path to your function app project, defaults to the repository root + DOTNET_VERSION: '8.0.x' # set this to the dotnet version to use (e.g. '2.1.x', '3.1.x', '5.0.x') + +jobs: + build-and-deploy: + runs-on: windows-latest + steps: + - name: 'Checkout GitHub Action' + uses: actions/checkout@v3 + + - name: Setup DotNet ${{ env.DOTNET_VERSION }} Environment + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: 'Resolve Project Dependencies Using Dotnet' + shell: pwsh + run: | + pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' + dotnet build --configuration Release --output ./output + popd + + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + id: fa + with: + app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }} + package: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/output' + publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }} \ No newline at end of file diff --git a/src/DataGeneratorFunction/.gitignore b/src/DataGeneratorFunction/.gitignore index ff5b00c..e9b4f59 100644 --- a/src/DataGeneratorFunction/.gitignore +++ b/src/DataGeneratorFunction/.gitignore @@ -144,7 +144,7 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings +# Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted #*.pubxml *.publishproj From b86a192c35d5ae2b575e42056bfc49012a890ae0 Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Wed, 9 Oct 2024 15:42:20 +0200 Subject: [PATCH 08/11] ci(function): make funtion configuration correct --- .../workflows/publish-data-generation-function.yaml | 2 +- terraform/application_insights.tf | 10 ++++++++++ terraform/function.tf | 5 ++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-data-generation-function.yaml b/.github/workflows/publish-data-generation-function.yaml index ac6a412..effcf2a 100644 --- a/.github/workflows/publish-data-generation-function.yaml +++ b/.github/workflows/publish-data-generation-function.yaml @@ -24,7 +24,7 @@ jobs: shell: pwsh run: | pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' - dotnet build --configuration Release --output ./output + dotnet publishw --configuration Release --output ./output popd - name: 'Run Azure Functions Action' diff --git a/terraform/application_insights.tf b/terraform/application_insights.tf index 2917027..4e88cca 100644 --- a/terraform/application_insights.tf +++ b/terraform/application_insights.tf @@ -3,4 +3,14 @@ resource "azurerm_application_insights" "apin" { location = azurerm_resource_group.data_platform.location resource_group_name = azurerm_resource_group.data_platform.name application_type = "web" + workspace_id = azurerm_log_analytics_workspace.log.id + } + +resource "azurerm_log_analytics_workspace" "log" { + name = "workspace-test" + location = azurerm_resource_group.data_platform.location + resource_group_name = azurerm_resource_group.data_platform.name + sku = "PerGB2018" + retention_in_days = 30 +} \ No newline at end of file diff --git a/terraform/function.tf b/terraform/function.tf index cff76bd..f46cde9 100644 --- a/terraform/function.tf +++ b/terraform/function.tf @@ -17,8 +17,11 @@ resource "azurerm_linux_function_app" "func" { service_plan_id = azurerm_service_plan.func.id site_config { application_insights_connection_string = azurerm_application_insights.apin.connection_string + always_on = true + application_stack { - dotnet_version = "8.0" + dotnet_version = "8.0" + use_dotnet_isolated_runtime = true } } } From 393708dab1fb0f18b2c827b7bac19af2a84b849a Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Wed, 9 Oct 2024 16:08:01 +0200 Subject: [PATCH 09/11] feat(function): send data to event hub --- .../DataGeneratorFunction.cs | 25 ++++++++++++++++--- .../DataGeneratorFunction.csproj | 2 ++ terraform/event_hub.tf | 25 +++++++++++++++++++ terraform/function.tf | 5 ++++ 4 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 terraform/event_hub.tf diff --git a/src/DataGeneratorFunction/DataGeneratorFunction.cs b/src/DataGeneratorFunction/DataGeneratorFunction.cs index 01064d8..d974931 100644 --- a/src/DataGeneratorFunction/DataGeneratorFunction.cs +++ b/src/DataGeneratorFunction/DataGeneratorFunction.cs @@ -1,3 +1,5 @@ +using System; +using System.Text.Json; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; @@ -13,15 +15,30 @@ public DataGeneratorFunction(ILoggerFactory loggerFactory) } [Function("DataGeneratorFunction")] - public void Run([TimerTrigger("*/1 * * * * *")] TimerInfo myTimer) + [EventHubOutput("dev-aquaplatform-ehn", Connection = "EventHubConnectionString")] + public string Run([TimerTrigger("*/1 * * * * *")] TimerInfo myTimer) { - Console.WriteLine("Running data generator function"); _logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}"); - + if (myTimer.ScheduleStatus is not null) { _logger.LogInformation($"Next timer schedule at: {myTimer.ScheduleStatus.Next}"); } + + // Generate random data + var randomData = new + { + Id = Guid.NewGuid(), + Timestamp = DateTime.UtcNow, + Value = new Random().Next(1, 100) + }; + + // Convert data to JSON + var eventHubMessage = JsonSerializer.Serialize(randomData); + + // Log generated data + _logger.LogInformation($"Generated data: {eventHubMessage}"); + return eventHubMessage; } } -} +} \ No newline at end of file diff --git a/src/DataGeneratorFunction/DataGeneratorFunction.csproj b/src/DataGeneratorFunction/DataGeneratorFunction.csproj index d9e46fd..399d4e4 100644 --- a/src/DataGeneratorFunction/DataGeneratorFunction.csproj +++ b/src/DataGeneratorFunction/DataGeneratorFunction.csproj @@ -8,7 +8,9 @@ + + diff --git a/terraform/event_hub.tf b/terraform/event_hub.tf new file mode 100644 index 0000000..07aa5b3 --- /dev/null +++ b/terraform/event_hub.tf @@ -0,0 +1,25 @@ + +resource "azurerm_eventhub_namespace" "this" { + name = "dev-aquaplatform-ehn" + location = azurerm_resource_group.data_platform.location + resource_group_name = azurerm_resource_group.data_platform.name + sku = "Standard" + capacity = 1 +} + +resource "azurerm_eventhub" "this" { + name = "dev-aquaplatform-eventhub" + namespace_name = azurerm_eventhub_namespace.this.name + resource_group_name = azurerm_resource_group.data_platform.name + partition_count = 2 + message_retention = 1 +} + +resource "azurerm_eventhub_namespace_authorization_rule" "this" { + name = "function-authorize-rule" + namespace_name = azurerm_eventhub_namespace.this.name + resource_group_name = azurerm_resource_group.data_platform.name + listen = false + send = true + manage = false +} diff --git a/terraform/function.tf b/terraform/function.tf index f46cde9..ed71890 100644 --- a/terraform/function.tf +++ b/terraform/function.tf @@ -24,4 +24,9 @@ resource "azurerm_linux_function_app" "func" { use_dotnet_isolated_runtime = true } } + app_settings = { + "EventHubConnectionString" = azurerm_eventhub_namespace_authorization_rule.this.primary_connection_string + } } + + From 6e18d7fa2ef852d60a5524e8a0d3399d40f9754c Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Wed, 9 Oct 2024 16:16:53 +0200 Subject: [PATCH 10/11] ci: fix whaterver type fix(function): make sure its the right event hub name --- .github/workflows/publish-data-generation-function.yaml | 2 +- src/DataGeneratorFunction/DataGeneratorFunction.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-data-generation-function.yaml b/.github/workflows/publish-data-generation-function.yaml index effcf2a..87c7978 100644 --- a/.github/workflows/publish-data-generation-function.yaml +++ b/.github/workflows/publish-data-generation-function.yaml @@ -24,7 +24,7 @@ jobs: shell: pwsh run: | pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' - dotnet publishw --configuration Release --output ./output + dotnet publish --configuration Release --output ./output popd - name: 'Run Azure Functions Action' diff --git a/src/DataGeneratorFunction/DataGeneratorFunction.cs b/src/DataGeneratorFunction/DataGeneratorFunction.cs index d974931..fdc159f 100644 --- a/src/DataGeneratorFunction/DataGeneratorFunction.cs +++ b/src/DataGeneratorFunction/DataGeneratorFunction.cs @@ -15,7 +15,7 @@ public DataGeneratorFunction(ILoggerFactory loggerFactory) } [Function("DataGeneratorFunction")] - [EventHubOutput("dev-aquaplatform-ehn", Connection = "EventHubConnectionString")] + [EventHubOutput("dev-aquaplatform-eventhub", Connection = "EventHubConnectionString")] public string Run([TimerTrigger("*/1 * * * * *")] TimerInfo myTimer) { _logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}"); From e19e00abc654ebd48a9a6987976cea8a947ee4d6 Mon Sep 17 00:00:00 2001 From: Peter Bull Hove Date: Wed, 30 Oct 2024 12:57:10 +0100 Subject: [PATCH 11/11] feat: make function run once every minute instead of every second --- src/DataGeneratorFunction/DataGeneratorFunction.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataGeneratorFunction/DataGeneratorFunction.cs b/src/DataGeneratorFunction/DataGeneratorFunction.cs index fdc159f..550d178 100644 --- a/src/DataGeneratorFunction/DataGeneratorFunction.cs +++ b/src/DataGeneratorFunction/DataGeneratorFunction.cs @@ -16,7 +16,7 @@ public DataGeneratorFunction(ILoggerFactory loggerFactory) [Function("DataGeneratorFunction")] [EventHubOutput("dev-aquaplatform-eventhub", Connection = "EventHubConnectionString")] - public string Run([TimerTrigger("*/1 * * * * *")] TimerInfo myTimer) + public string Run([TimerTrigger("0 */1 * * * *")] TimerInfo myTimer) { _logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}"); @@ -41,4 +41,4 @@ public string Run([TimerTrigger("*/1 * * * * *")] TimerInfo myTimer) return eventHubMessage; } } -} \ No newline at end of file +}