Skip to content

Commit

Permalink
Added Farmer deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
Ayrat Hudaygulov committed Dec 15, 2020
1 parent 1181a46 commit d0ee704
Show file tree
Hide file tree
Showing 5 changed files with 369 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/azure-deploy-all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Deploy everything to Azure

on: workflow_dispatch

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Deploy everything to Azure
run: |
cd src/Grinder.Farmer
dotnet run --all
118 changes: 118 additions & 0 deletions src/Grinder.Farmer/.farmer/farmer-deploy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {
"docker-password-for-vahterregistry": {
"type": "securestring"
}
},
"resources": [
{
"apiVersion": "2020-03-01-preview",
"location": "northeurope",
"name": "vahter-log",
"properties": {
"publicNetworkAccessForIngestion": "Enabled",
"publicNetworkAccessForQuery": "Enabled",
"retentionInDays": 30,
"sku": {
"name": "PerGB2018"
}
},
"tags": {},
"type": "Microsoft.OperationalInsights/workspaces"
},
{
"apiVersion": "2016-08-01",
"dependsOn": [
"vahter-app-farm",
"vahter-log"
],
"identity": {
"type": "None"
},
"kind": "app,linux,container",
"location": "northeurope",
"name": "vahter-app",
"properties": {
"httpsOnly": false,
"serverFarmId": "vahter-app-farm",
"siteConfig": {
"alwaysOn": true,
"appCommandLine": "",
"appSettings": [
{
"name": "DOCKER_ENABLE_CI",
"value": "true"
},
{
"name": "DOCKER_REGISTRY_SERVER_PASSWORD",
"value": "[parameters('docker-password-for-vahterregistry')]"
},
{
"name": "DOCKER_REGISTRY_SERVER_URL",
"value": "https://vahterregistry.azurecr.io"
},
{
"name": "DOCKER_REGISTRY_SERVER_USERNAME",
"value": "vahterregistry"
},
{
"name": "VAHTER_CONFIG",
"value": "{\r\n \"Bot\": {\r\n \"Token\": \"800514531:AAH12BMMpXJvuvtdj_6p4UHWszVs6101I5I\",\r\n \"ChannelId\": -1001492656505,\r\n \"AdminUserId\": 67509832,\r\n \"ChatsToMonitor\": [\r\n \"@fsharp_flood\",\r\n \"@dotnetby\",\r\n \"@cilchat\",\r\n \"@comput_math\",\r\n \"@higher_math\",\r\n \"@elasticsearch_ru\",\r\n \"@Avalonia\",\r\n \"@fsharp_jobs\",\r\n \"@pro_latex\",\r\n \"@fsharp_chat\",\r\n \"@pro_net\",\r\n \"@DotNetRuChat\",\r\n \"@dotnettalks\",\r\n \"@DotNetRuJobs\",\r\n \"@DotNetChat\",\r\n \"@powershell_pro\"\r\n ],\r\n \"AllowedUsers\": [\r\n \"Liminiens\",\r\n \"fvnever\",\r\n \"aensidhe\",\r\n \"etkee\",\r\n \"EgorBo\",\r\n \"neftedollar\",\r\n \"zawodskoj\",\r\n \"kekekeks\",\r\n \"ahydrax\",\r\n \"hacklex\",\r\n \"striped\",\r\n \"XaveScor\",\r\n \"omgszer\",\r\n \"grishace\",\r\n \"AnutaU\"\r\n ]\r\n }\r\n}"
}
],
"connectionStrings": [],
"linuxFxVersion": "DOCKER|vahterregistry.azurecr.io/vahter/grinder:latest",
"metadata": []
}
},
"tags": {},
"type": "Microsoft.Web/sites"
},
{
"apiVersion": "2018-02-01",
"kind": "linux",
"location": "northeurope",
"name": "vahter-app-farm",
"properties": {
"name": "vahter-app-farm",
"perSiteScaling": false,
"reserved": true
},
"sku": {
"capacity": 1,
"name": "B1",
"size": "0",
"tier": "Basic"
},
"tags": {},
"type": "Microsoft.Web/serverfarms"
},
{
"type": "Microsoft.Web/sites/providers/diagnosticSettings",
"apiVersion": "2017-05-01-preview",
"name": "[concat('vahter-app', '/microsoft.insights/', 'vahter-log')]",
"dependsOn": [],
"properties": {
"workspaceId": "[resourceId('Microsoft.OperationalInsights/workspaces', 'vahter-log')]",
"metrics": [],
"logs": [
{
"category": "AppServiceConsoleLogs",
"enabled": true
},
{
"category": "AppServiceAppLogs",
"enabled": true
},
{
"category": "AppServiceHTTPLogs",
"enabled": true
}
]
}
}
]
}
22 changes: 22 additions & 0 deletions src/Grinder.Farmer/Grinder.Farmer.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<Compile Include="Program.fs" />
<Content Include="settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<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

0 comments on commit d0ee704

Please sign in to comment.