Continuous delivery for a FSharp Suave.io REST API with FAKE Deploy
Introduction
What’s Suave.io ?
If your first question is “What’s Suave.io” or “Why would I use it ?” then I suggest you to read those slides
What’s FAKE?
Fake is a DSL permitting to write advanced automated builds in FSharp. It can be used by beginners in FSharp because the DSL can hide some FSharp code comlexity. We can define multiple targets like in a Makefile.
What’s FAKE Deploy ?
FAKE Deploy is an agent like MSDeploy installable on your servers. His role is to receive your packaged server applications and install it running a FSHARP deployment script.
Why should I use it instead of another tool ?
- FAKE is a DSL simplifying FSharp writing.
- You can use every .Net library in your build or deployment scripts.
- You have a really great feedback and testability to write your scripts.
Installing FAKE Deploy agent
Everything is documented here, it’s quite simple.
Open a PowerShell as Administrator:
PS> mkdir c:\fake_deploy
PS> cd c:\fake_deploy
PS> nuGet.exe Install FAKE -ExcludeVersion
PS> cd .\FAKE\tools\
PS> .\Fake.Deploy.exe /install
PS> (iwr http://localhost:8080/fake/deployments/).Content
{"Case":"QueryResult","Fields":[[]]}
Edit C:\fake_deploy\FAKE\tools\Fake.Deploy.exe.config
and set C:\fake_deploy\deployments
as WorkDirectory
Restart FAKE Deploy Agent
windows service.
Writing build script
We will work on this project. MailCheckerRestApi is a REST Api checking emails validities connecting to their SMTP and starting a sending operation.
Build Script is versionned here
Project directory structure is:
“Deployment” folder contains scripts and resources used by agent on server to install service. Application is using Topshelf for his Windows Service hosting.
- deploy.nuspec is a basic NuGet package template. Agent is waiting for a package upload.
- deployService.fsx is the script ran by the FAKE Deploy agent.
- refs.fs contains libraries includes. It will be replaced in the package to respect futur directory three structure.
- ServiceManifest.json contains configurations for deployment environment. (Local, Preprod, Prod, etc …)
So now look at the build.fsx …
let packFakeDeploy () =
XCopyHelper.XCopy (__SOURCE_DIRECTORY__ @@ "packages" @@ "build" @@ "FSharp.Data" @@ "lib" @@ "net40") (buildDir @@ "_deploy" @@ "packages" @@ "FSharp.Data")
XCopyHelper.XCopy (__SOURCE_DIRECTORY__ @@ "packages" @@ "build" @@ "FAKE" @@ "tools") (buildDir @@ "_deploy" @@ "packages" @@ "FAKE")
(__SOURCE_DIRECTORY__ @@ "Deployment" @@ "_deploy" @@ "deployService.fsx") |> CopyFile (buildDir @@ "deploy.fsx")
(__SOURCE_DIRECTORY__ @@ "Deployment" @@ "_deploy" @@ "ServiceManifest.json") |> CopyFile (buildDir @@ "ServiceManifest.json")
WriteFile (buildDir @@ "refs.fsx")
[ """#r @"_deploy/packages/FAKE/FakeLib.dll" """
"""#r @"_deploy/packages/FSharp.Data/FSharp.Data.dll" """ ]
NuGet (
fun p ->
{ p with
Authors = ["rflechner"]
Project = "MailTest"
Title = "MailTest"
Description = "REST service checking email validity on their SMTP"
OutputPath = deployDir
WorkingDir = buildDir
Version = "1.0" //TODO: get version of sprint
Publish = false
Files =
[
"MailTester/**", Some "/", None
"_deploy/**", Some "/", None
]
Dependencies = [ ]
})
(__SOURCE_DIRECTORY__ @@ "Deployment" @@ "deploy.nuspec")
packFakeDeploy ()
function is packaging the compiled application in the nuget
let uploadPackage url nupkg =
ExecProcess (fun info ->
info.FileName <- (__SOURCE_DIRECTORY__ @@ "packages" @@ "build" @@ "FAKE" @@ "tools" @@ "Fake.Deploy.exe")
info.Arguments <- (sprintf "/deployRemote %s %s" url nupkg)
info.WorkingDirectory <- __SOURCE_DIRECTORY__
) (System.TimeSpan.FromMinutes 5.) |> ignore
uploadPackage
function run Fake.Deploy.exe
to upload compiled application
Then we can create targets dedicated to a local deployment for example:
Target "Local-Pack-Service" (fun _ ->
packFakeDeploy ()
)
Target "Local-Install-Service" (fun _ ->
let nupkg = deployDir |> directoryInfo |> filesInDirMatching "*.nupkg" |> Seq.head
uploadPackage "http://localhost:8080/fake" nupkg.FullName
)
Writing deployment script
In deployService.fsx
I used FAKE because there are a lot of helpers like XCopy
, ProcessHelper
, etc…
I used a simple JSON typeprovider to parse ServiceManifest.json
.
addUrlAcl
is creating Url ACL to authorize Suave to create a HTTP endpoint with the service impersonation.
let addUrlAcl url username =
let arg = sprintf """http add urlacl url=%s user=%s""" url username
ExecProcessElevated "netsh" arg timeout |> ignore
It is really cool to write this script in FSharp because it’s easy to test and debug. We don’t need to upload the entire package to test a change (a simple ALT + ENTER in the IDE is sufficient)
Test the service install
Clone project and open your powershell as Administrator.
PS> cd my_project_path
PS> .\build.cmd Local-Install-Service
Then go to http://localhost:8285/swagger/v2/ui/index.html
Using it on a production server
When you install an agent on a production server, you should read Security
section of http://fsharp.github.io/FAKE/deploy.html to enable authorization keys.
I suggest to restrict IP access to your continuous delivery server.