Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Added Farmer deployment #41

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
bin/
obj/
**/bin/
**/obj/
out/
TestResults/
20 changes: 20 additions & 0 deletions .github/workflows/azure-deploy-all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Deploy everything to Azure

on: workflow_dispatch

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Deploy everything to Azure
env:
VAHTER_DEPLOY_APPID: ${{ secrets.VAHTER_DEPLOY_APPID }}
VAHTER_DEPLOY_PWD: ${{ secrets.VAHTER_DEPLOY_PWD }}
VAHTER_DEPLOY_TENANT: ${{ secrets.VAHTER_DEPLOY_TENANT }}
VAHTER_CONFIG: ${{ secrets.VAHTER_CONFIG }}
run: |
cd src/Grinder.Farmer
dotnet run --all
26 changes: 9 additions & 17 deletions .github/workflows/azure-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,13 @@ jobs:
runs-on: ubuntu-latest

steps:
- name: Docker Login
uses: Azure/docker-login@v1
with:
username: ${{ secrets.AZURE_DOCKER_REGISTRY_USER }}
password: ${{ secrets.AZURE_DOCKER_REGISTRY_PASSWORD }}
login-server: https://dotnetru.azurecr.io/v1

- uses: actions/checkout@v2

- name: Build Docker image
run: docker build . -t grinder

- name: Tag Docker image
run: docker tag grinder dotnetru.azurecr.io/vahter/grinder

- name: Push Docker image
run: docker push dotnetru.azurecr.io/vahter/grinder

- name: Build and push docker image to registry
env:
VAHTER_DEPLOY_APPID: ${{ secrets.VAHTER_DEPLOY_APPID }}
VAHTER_DEPLOY_PWD: ${{ secrets.VAHTER_DEPLOY_PWD }}
VAHTER_DEPLOY_TENANT: ${{ secrets.VAHTER_DEPLOY_TENANT }}
VAHTER_CONFIG: ${{ secrets.VAHTER_CONFIG }}
run: |
cd src/Grinder.Farmer
dotnet run
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,7 @@ __pycache__/
*.db
/bot_config.json
*.orig

# Bot deployment settings
/src/Grinder.Farmer/settings.json
/src/Grinder.Farmer/.farmer
1 change: 0 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
<TieredCompilation>true</TieredCompilation>
<DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference>
<MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors>
<LangVersion>preview</LangVersion>
</PropertyGroup>
</Project>
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ COPY src/Grinder.Common/Grinder.Common.fsproj ./src/Grinder.Common/Grinder.Commo
COPY src/Grinder.DataAccess/Grinder.DataAccess.csproj ./src/Grinder.DataAccess/Grinder.DataAccess.csproj
COPY tests/Grinder.Tests/Grinder.Tests.fsproj ./tests/Grinder.Tests/Grinder.Tests.fsproj
COPY src/Grinder.ExportTool/Grinder.ExportTool.fsproj ./src/Grinder.ExportTool/Grinder.ExportTool.fsproj
COPY src/Grinder.Farmer/Grinder.Farmer.fsproj ./src/Grinder.Farmer/Grinder.Farmer.fsproj
COPY Directory.Build.props ./Directory.Build.props
RUN dotnet tool restore
RUN dotnet restore -r linux-x64
Expand Down
6 changes: 6 additions & 0 deletions Grinder.sln
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Grinder.Farmer", "src\Grinder.Farmer\Grinder.Farmer.fsproj", "{1B7B3688-F966-4F62-BDCA-ADF56B4E3160}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -50,6 +52,10 @@ Global
{13E2DB48-FA8E-48A1-AD53-DA81EBE63A09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13E2DB48-FA8E-48A1-AD53-DA81EBE63A09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13E2DB48-FA8E-48A1-AD53-DA81EBE63A09}.Release|Any CPU.Build.0 = Release|Any CPU
{1B7B3688-F966-4F62-BDCA-ADF56B4E3160}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B7B3688-F966-4F62-BDCA-ADF56B4E3160}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B7B3688-F966-4F62-BDCA-ADF56B4E3160}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B7B3688-F966-4F62-BDCA-ADF56B4E3160}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
3 changes: 2 additions & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"sdk": {
"version": "5.0.101"
"version": "5.0.101",
"rollForward": "latestMajor"
}
}
19 changes: 19 additions & 0 deletions src/Grinder.Farmer/Grinder.Farmer.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.fs" />
<Content Include="README.MD" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FSharp.Core" Version="5.0.0" />
<PackageReference Include="Farmer" Version="1.2.0" />
<PackageReference Include="MedallionShell" Version="1.6.2" />
</ItemGroup>

</Project>
185 changes: 185 additions & 0 deletions src/Grinder.Farmer/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
open System
open System.IO
open Farmer
open Farmer.Builders
open Medallion.Shell

let resourceGroup = "vahter-rg"
let acrName = "vahterregistry"
let logName = "vahter-log"
let appName = "vahter-app"

let getEnv name =
match Environment.GetEnvironmentVariable name with
| null ->
failwith $"Provide required ENV variable: {name}"
// Console.WriteLine $"Please provide ENV variable: {name}"
// Console.ReadLine()
| value -> value

let appId = getEnv "VAHTER_DEPLOY_APPID"
let pwd = getEnv "VAHTER_DEPLOY_PWD"
let tenant = getEnv "VAHTER_DEPLOY_TENANT"

type Result.ResultBuilder with
member _.Bind(cmd: Command, next: unit -> Result<'a, string>): Result<'a, string> =
cmd.StandardOutput.PipeToAsync(Console.Out, true) |> ignore
cmd.StandardError.PipeToAsync(Console.Error, true) |> ignore
if cmd.Result.Success then
next()
else
Error cmd.Result.StandardError

let botSettings =
let vahterConfig = Environment.GetEnvironmentVariable "VAHTER_CONFIG"
if File.Exists "./settings.json" then
File.ReadAllText "settings.json"
else if vahterConfig <> null then
vahterConfig
else
failwith "Please put bot settings either 1) in settings.json 2) or in env VAHTER_CONFIG"

let logs = logAnalytics {
name logName

retention_period 30<Days>
enable_query
enable_ingestion
}

let registry = containerRegistry {
name acrName

sku ContainerRegistry.Basic
enable_admin_user
}

let botApp = webApp {
name appName

app_insights_off
always_on
operating_system Linux
sku WebApp.Sku.B1

setting "VAHTER_CONFIG" botSettings

docker_ci
docker_use_azure_registry acrName
docker_image "vahter/grinder:latest" ""

depends_on logs.Name
}

let registryDeployment = arm {
location Location.NorthEurope
add_resource registry
output "host" registry.LoginServer
output "pwd" $"[listCredentials(resourceId('Microsoft.ContainerRegistry/registries','{acrName}'),'2017-10-01').passwords[0].value]"
output "login" $"['{acrName}']"
}

let appDeployment = arm {
location Location.NorthEurope
add_resources [
logs
botApp
]
add_resource (Resource.ofJson $"""
{{
"type": "Microsoft.Web/sites/providers/diagnosticSettings",
"apiVersion": "2017-05-01-preview",
"name": "[concat('{appName}', '/microsoft.insights/', '{logName}')]",
"dependsOn": [],
"properties": {{
"workspaceId": "[resourceId('Microsoft.OperationalInsights/workspaces', '{logName}')]",
"metrics": [],
"logs": [
{{
"category": "AppServiceConsoleLogs",
"enabled": true
}},
{{
"category": "AppServiceAppLogs",
"enabled": true
}},
{{
"category": "AppServiceHTTPLogs",
"enabled": true
}}
]
}}
}}
""")
}

let getAcrCreds() = result {
let azShell = Shell(fun opts ->
opts.ThrowOnError(false)
|> ignore
)
do! azShell.Run("az", "login", "--service-principal", "-u", appId, "-p", pwd, "--tenant", tenant)
let pwd = azShell.Run("az", "acr", "credential", "show", "--name", acrName, "--query", "passwords[0].value")
let pwdStringRaw = pwd.Result.StandardOutput
let pwdString = pwdStringRaw.Substring(1, pwdStringRaw.Length - 3) // truncating first and last chars
return $"{acrName}.azurecr.io", acrName, pwdString
}

let pushDockerImage (host, user, pwd) = result {
let dockerShell = Shell(fun opts ->
opts.ThrowOnError(false)
.WorkingDirectory("../..")
|> ignore
)

do! dockerShell.Run("docker", "login", "-u", user, "-p", pwd, host)
do! dockerShell.Run("docker", "build", ".", "-t", "grinder")
do! dockerShell.Run("docker", "tag", "grinder", $"{host}/vahter/grinder")
do! dockerShell.Run("docker", "push", $"{host}/vahter/grinder")
return ()
}

let deployAll() = result {
// authenticate into Azure
let! authResult = Deploy.authenticate appId pwd tenant
printfn "%A" authResult

// deploying container registry
let! registryDeploymentResult =
Deploy.tryExecute
resourceGroup
Deploy.NoParameters
registryDeployment

let registryPwd = registryDeploymentResult.["pwd"]
let registryLogin = registryDeploymentResult.["login"]
let registryHost = registryDeploymentResult.["host"]

// build&push image to registry
do! pushDockerImage(registryHost, registryLogin, registryPwd)

// deploy webapp with bot
let! deploymentResult =
Deploy.tryExecute
resourceGroup
[ botApp.DockerAcrCredentials.Value.Password.Value, registryPwd ]
appDeployment
return printfn "%A" deploymentResult
}

[<EntryPoint>]
let main argv =
match argv with
| null | [||] ->
// deploy only image
result {
let! host, usr, pwd = getAcrCreds()
do! pushDockerImage(host, usr, pwd)
}
| x when Array.contains "--all" x ->
// deploy everything = resources + image
deployAll()
| x ->
failwithf "Unknown arguments %A" x
|> Result.get
0
28 changes: 28 additions & 0 deletions src/Grinder.Farmer/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#Description
This project will deploy Grinder bot to Azure
There are two scenarios
- Deploy Azure resources together with docker image
- Build and push docker image only into predeployed container registry

To deploy everything pass `--all` argument

To deploy just an image don't pass any argument

#Prerequisites
- AZ CLI
- Docker
- Net5 SDK

- Azure credentials (put them in env variables)
- `VAHTER_DEPLOY_APPID`
- `VAHTER_DEPLOY_PWD`
- `VAHTER_DEPLOY_TENANT`

- Bot settings (pick one)
- `settings.json` file
- `VAHTER_CONFIG` variable

#Run
`dotnet run [args]` (from project folder)

or just run from IDE
2 changes: 0 additions & 2 deletions src/Grinder/Commands.fs
Original file line number Diff line number Diff line change
Expand Up @@ -584,8 +584,6 @@ module Processing =

return Some <| UnbanOnReplyMessage(context.From, message, errors)
| PingCommand context ->
sprintf "Sending PONG to %A" context.ChatId
|> logInfo
do! botApi.SendTextMessage context.ChatId "pong"
return None
| DoNothingCommand ->
Expand Down
20 changes: 10 additions & 10 deletions src/Grinder/Grinder.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FParsec" Version="1.0.3" />
<PackageReference Include="FParsec" Version="1.1.1" />
<PackageReference Include="FSharp.Core" Version="5.0.0" />
<PackageReference Include="FSharp.UMX" Version="1.0.0-preview-001" />
<PackageReference Include="FSharp.UMX" Version="1.0.0" />
<PackageReference Include="Funogram" Version="1.3.1" />
<PackageReference Include="HttpToSocks5Proxy" Version="1.1.3" />
<PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="3.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.2.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="HttpToSocks5Proxy" Version="1.4.0" />
<PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="4.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
</ItemGroup>
Expand Down
Loading