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:

mailtester_dir_three1

“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

swagger-mail01

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.