Working in a microservice approach using Azure Service Fabric is great ! However, working with a collection of applications that depend on each other can be really a pain when it’s time to debug them locally (in a local Service Fabric cluster).  To debug one application, we have to deploy all applications linked…. Let’s find a solution using a simple PowerShell script.

 

Context

I have a five independant microservices that work together to achieve mailing processes. They use the SF remoting stack to communicate with each other.

I have a git repository per app:

 

 

Using Visual Studio to deploy applications

If I want to debug one of my “mailing” application, I need to deploy at least 3 applications linked. I need to open one instance of Visual studio per app to deploy them in a parallel way, it works but it’s not a quick solution. When I will reset my local cluster, I will have to do this operation again… and again.

To publish a Service Fabric application, Visual studio follows 2 steps:

  • Package the app using MS build
  • Deploy the package to the local or remote cluster using the Deploy-FabricApplication.ps1

 

Here is a screenshot of my solution explorer in Visual Studio :

 

The Deploy-FabricApplication.ps1 script deploys or upgrades an application reading connexion information in publishprofile.*.xml files.

This script uses two linked PowerShell scripts:

  • Publish-NewServiceFabricApplication
  • Publish-UpgradedServiceFabricApplication

They can be found in the following local folder: C:\Program Files\Microsoft SDKs\Service Fabric\Tools\PSModule\ServiceFabricSDK

The Publish-NewServiceFabricApplication is useful to work with Visual Studio: it does a lot of check on files (application parameters, publish profiles, applicationManifest) and give us a deployment status during the deployment operation. However it does not package the application. Let’s create a simplest script that will just package the app and publish it in the local cluster.

 

Home made script

Our script contains 3 functions:

  • Read-XmlElementsAsHashtable: Parses an application parameter file in an hashtable
  • Read-XmlElementAsHashtable: Parses the application manifest file in an hastable
  • Deploy-App: let’s explain it in details below 😊

 

The “Deploy-App” function

This function is going to execute two main jobs:

  1. Package the app using MsBuild
  2. Deploy the app in the local Service Fabric cluster

An SF application deployment can be done following 3 steps:

  1. Store the app package in the SF store (Copy-ServiceFabricApplicationPackage)
  2. Register the new application type (Register-ServiceFabricApplicationType)
  3. Create the application instance (New-ServiceFabricApplication)

Before to execute these 3 steps, the function will first try to find the *.sfproj file. With this file we are able to find all others files we need:

  • The application manifest file path
  • The application parameter file path
  • The package folder path

 

function Read-XmlElementsAsHashtable
{
    Param (
        $Elements
    )

    $hashtable = @{}

    foreach($Element in $Elements){

        if ($Element.Attributes){        
            
            $name = $Element.Attributes[0].Value
            $value = $Element.Attributes[1].Value

            $boolVal=$null
            if ([bool]::TryParse($value, [ref]$boolVal)){
                  $hashtable[$name] = $boolVal
            }
            else {
               $hashtable[$name] = $value
            }
        }
    }

    return $hashtable
}

function Read-XmlElementAsHashtable
{
    Param (
        [System.Xml.XmlElement]
        $Element
    )

    $hashtable = @{}
    if ($Element.Attributes)
    {
        $Element.Attributes | 
            ForEach-Object {
                $boolVal = $null
                if ([bool]::TryParse($_.Value, [ref]$boolVal)) {
                    $hashtable[$_.Name] = $boolVal
                }
                else {
                    $hashtable[$_.Name] = $_.Value
                }
            }
    }

    return $hashtable
}

function Deploy-App
{
  Param (       
        
        [Parameter(Mandatory=$true)]
        [string] $AppFolder,

        [Parameter(Mandatory=$true)]
        [string] $Env,

        [Parameter(Mandatory=$true)]
        [string] $MsBuildPath,

        [Parameter(Mandatory=$false)]
        [string] $BuildModel = 'Debug'
    ) 


Write-Host '*********************** Working in' $AppFolder

$sfProjFileObject = Get-Childitem -Recurse -Path $AppFolder -Filter '*.sfproj' -ErrorAction SilentlyContinue | Sort-Object Length -Descending | ForEach-Object {
    $_.BaseName                                                              # service name
    $_.FullName                                                              # sfproj path
    $_.DirectoryName                                                         # sfproj file directory
    $_.DirectoryName + '\ApplicationParameters\' + $Env + '.xml'             # application parameter file path
    $_.DirectoryName + '\pkg\' + $BuildModel.ToLower()                     # package path
    $_.DirectoryName + '\ApplicationPackageRoot\ApplicationManifest.xml'    # application manifest file path
}

    if ($sfProjFileObject){
   
          $_appName = $sfProjFileObject[0]
          $_appType = $_appName + 'Type'
          $_appFabricName = 'fabric:/' + $_appName
          $_sfprojPath = $sfProjFileObject[1]
          $_applicationParamtersPath = $sfProjFileObject[3]
          $_pkgPath = $sfProjFileObject[4]
          $_applicationPackageRootFilePath = $sfProjFileObject[5]
          $_timeOut = 1800   # 30 min

          # Package the app
          $msBuildBuildParam = '/p:Configuration=' + $BuildModel.ToLower()
          $collectionOfArgs = @($_sfprojPath, '/target:Package', $msBuildBuildParam)
          & $MsBuildPath $collectionOfArgs

          ########### Test the SF package ###########
          $_packageIsValid = Test-ServiceFabricApplicationPackage -ApplicationPackagePath $_pkgPath

          if ($_packageIsValid)
          {
                write-host '***** Deploying' $_appName '*****'

                ########### Copy the package in the store ###########
                write-host '-- Copy package'
                Copy-ServiceFabricApplicationPackage -ApplicationPackagePath $_pkgPath -ApplicationPackagePathInImageStore $_appName -ShowProgress
    
                # Register the application type.
                write-host '-- Register the app' $_appName 
                Register-ServiceFabricApplicationType -ApplicationPathInImageStore $_appName -TimeoutSec $_timeOut

                # Create the application instance
                write-host '-- Create the app instance' $_appType $_appFabricName

                # Parse application Manifer
                $_appManifest = [Xml] (Get-Content $_applicationPackageRootFilePath) 
                $_appManifestContent = Read-XmlElementAsHashtable $_appManifest.ApplicationManifest    
                $_version =  $_appManifestContent['ApplicationTypeVersion']

                # Parse application paramters
                $_applicationParamters = [Xml] (Get-Content $_applicationParamtersPath) 
                $_params = Read-XmlElementsAsHashtable $_applicationParamters.Application.Parameters.ChildNodes
                New-ServiceFabricApplication -ApplicationName $_appFabricName -ApplicationParameter $_params -ApplicationTypeName $_appType -ApplicationTypeVersion $_version -TimeoutSec $_timeOut
    
                write-host '-- Remove app package'
                # Remove the application package to free system resources.
                Remove-ServiceFabricApplicationPackage -ApplicationPackagePathInImageStore $_appName
        }
        else{
             write-host '-- Package is not valid'
        }
    }
    else{
        write-host 'No .sfproj file was found'
    }
}

 

To use the the Deploy-All function we need to give it 3 parameters:

  • The msbuild path
  • The folder where our application is stored
  • The application parameter file name we want to use
$msbuildPath = 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe'
$sfRepo = 'C:\Repositories\******\SF'
$envTarget = 'Local.1Node'
$appFolder = $sfRepo + '\******_SF_AdminNotification'

Connect-ServiceFabricCluster

######## Exemple of how to deploy a service from a local repo
Deploy-App -AppFolder $appFolder  -Env $envTarget -MsBuildPath $msbuildPath

 

Happy coding 🙂

 


Hosting an Elastic Search log server in a serverless Architecture – Azure Service Fabric Mesh

Leave a Reply

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *