Azure Functions y múltiples archivos de script en F#

Nota 1: Realmente no tiene nada que ver con los Azure Functions pero como aquí se usan Scripts de F# fue la excusa perfecta para aprenderlo.

La semana pasada se anunció un nuevo release de Azure Functions donde destacan el soporte mejorado para F#

image

Y esta misma semana un colega se tomó el tiempo para escribir un excelente tutorial (en inglés) con su respectivo ejemplo de código, donde muestra cómo crear una Azure Function con F# desde cero.

Ayer me animé a migrar el proyecto que había inspirado la anterior entrada a esta nueva versión con soporte mejorado para F# y esto fue lo que aprendí.

En la versión anterior, como había comentado, tenía un fsproj que ahora debía moverse a archivos de script de F# (aka .fsx). Mover los archivos a la nueva carpeta y cambiarles la extensión es la parte fácil. Ahora que hay archivos de Script en lugar de namespaces es necesario cargar los scripts a reutilizar con la directiva #load (como lo haríamos en los Scripts de C#) y abrir los módulos que se necesiten… pero esto solo es en teoría… porque me costó un poco más.

Trabajar con múltiples archivos de Script de F#

Nota 2: Esto no es solo para los fsx, es en general de F#, pero con los namespaces en  los fsproj y con las ayuda de VS no me era claro y lo desconocía.

Suponiendo una estructura de archivos como la siguiente:

operaciones.fsx
tiposOps.fsx (carga operaciones.fsx)

Si el código en operaciones.fsx quedara así

module Basicas =
    let sumar x y = x + y

El código en tiposOps.fsx genera el error

image

¿Por qué? Pues esto me pasa por obviar paginas de la documentación. Resulta que hay dos tipos de módulos, los top-level y los locales. Los top-level pueden aparecer únicamente como la primera declaración en un archivo1 y no llevan el signo igual, signo que si llevan los locales o anidados. Cuando no se usa un modulo top-level todo el código dentro del archivo se agrupa en un modulo creado automáticamente con el mismo nombre del archivo, sin extensión y con la primera letra en mayúscula.

Solo puede haber una función Run

Nota 3: Esto si tiene que ver con Azure Functions

Solo se puede nombrar una función run, de lo contrario recibiremos el mensaje de error error AF002: Multiple methods named 'Run'. Consider renaming methods.

Conclusiones

Con esta información podemos concluir que:

  • El código en el archivo operaciones.fsx usa un modulo local y al intentar usarlo se debería hacer de la forma open Operaciones.Basicas o mejor, cambiar la declaración del modulo para que sea un top-level. En lo personal no me gusta usar el modulo que se crea automáticamente y prefiero hacerlo explicito.
  • La documentación está para leerla :c

Referencias y enlaces de interés

1 Modules, Microsoft Docs

Defining Modules VS.NET vs F# Interactive, SO

Interactive Programming with F#, Microsoft Docs

Should F# functions be placed in modules, classes, or another structure?, SO

Espero les sea de utilidad.

Hasta el próximo post.

Anuncios
Azure Functions y múltiples archivos de script en F#

How To: Despliegue de Azure Function desde VSTS usando Kudu

En los Azure Functions existe la posibilidad de configurar integración continua desde alguno de los repositorios soportados, y funciona muy bien en el escenario normal (con la estructura de la función y los archivos de script). Sin embargo, puede darse el caso que se necesite compilar previamente un proyecto para usar sus ensamblados desde el script de la función. Suena un poco raro, pero este fue mi caso la semana pasada dado el estado actual de soporte para F# (en el que se está avanzando). La situación era un proyecto de consola (.exe) que será llamado desde una función (Powershell) con un TimerTrigger.

Al final es muy fácil, pero perdí algo de tiempo poniendo las piezas juntas, así que dejo aquí el procedimiento solo por si me vuelve a ocurrir o le es de utilidad a alguien más:

1- A la definición del Build de Visual Studio de VSTS agregar un paso de copia de archivos con las propiedades como se ven en la imagen. Esto copiará la función (el archivo run y el function.json) al lugar donde ha sido ubicado el binario previamente (en mi caso, la función no hace parte del proyecto)

image

 

2- Agregar un paso para crear el zip que será enviado al endpoint de Kudu. Las propiedades las he dejado como están en la imagen:

image

3- Al repositorio se le debe agregar un archivo de Powershell, en mi caso lo he nombrado deploy.ps1 con el siguiente código:

Param(
    [Parameter(Mandatory = $true)]
    [string]$websiteName,

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

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

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

    [string]$destinationPath = "/site/wwwroot/{nombre de la funcion}",
    [int]$timeout = 600
)

$ErrorActionPreference = "Stop"

Function JoinParts {
    param ([string[]] $Parts, [string] $Separator = '/')

    $search = '(?<!:)' + [regex]::Escape($Separator) + '+'
    ($Parts | Where-Object {$_ -and $_.Trim().Length}) -join $Separator -replace $search, $Separator
}

#Select-AzureSubscription -SubscriptionId $subscriptionId

$website = Get-AzureWebsite -Name $websiteName

#$username = $website.PublishingUsername
#$password = $website.PublishingPassword

$base64Auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password)))
$authHeader = @{Authorization=("Basic {0}" -f $base64Auth)}

$baseUri = ($website.SiteProperties.Properties | Where-Object { $_.Name -eq "RepositoryUri" } | Select-Object -First 1).Value

$commandApiUri = JoinParts ($baseUri, "/api/command")
$commandBody = @{
    command = "rmdir {nombre de la funcion} /q /s && mkdir {nombre de la funcion}"
    dir = "site\wwwroot"
}

Write-Output "Removing previous build..."
Invoke-RestMethod -Uri $commandApiUri -Headers $authHeader -Method POST -ContentType "application/json" -Body (ConvertTo-Json $commandBody) -TimeoutSec $timeout | Out-Null

Write-Output ("Publishing to URI '{0}'..." -f $deployApiUri)
$deployApiUri = JoinParts ($baseUri, "api/zip", $destinationPath) '/'
Invoke-RestMethod -Uri $deployApiUri -Headers $authHeader -Method PUT -InFile $buildZip -ContentType "multipart/form-data" -TimeoutSec $timeout | Out-Null

Este código no es mío, lo he adaptado de este articulo que me aclaró bastante el panorama.

Básicamente lo que hace es:

3.1- Usar algunos cmdlets del Azure PowerShell para recuperar un Azure WebSite a partir de su nombre (si, un Azure Website). Estos cmdlets los tenemos disponibles en VSTS.

3.2- A partir de sus propiedades recupera la url del scm

3.3- Hace una petición POST al endpoint command para eliminar el contenido de la función anterior

3.4- Hace un PUT al endpoint zip con el zip que creamos en el paso anterior.

Nota: Agregué el usuario y el password como parámetros del script porque, al parecer, ya no hacen parte de las propiedades del Website. Este usuario y contraseña son los deployment credentials del sitio.

4- Vincular el Azure con VSTS

5- Agregar un paso de Azure PowerShell con las propiedades como se ven en la imagen:

image

 

Referencias

Deploying to Azure Using Kudu

Deploy to azure functions using powershell

Espero les sea de utilidad.

Hasta el próximo post.

How To: Despliegue de Azure Function desde VSTS usando Kudu

Serverless Microservices con Azure Functions y Api Management

Hace ya un tiempo que Amazon liberó el servicio Lambda, en ese entonces no lo entendí muy bien, me pareció entender que respondía a eventos, lo comparé con los Azure WebSites + Azure Web Jobs y dejé el tema hasta ahí. En las ultimas semanas Microsoft anunció el servicio Azure Functions, y en la web ya se veían comparaciones con AWS Lambda, vi la documentación y la presentación en su momento, pero no entendí que plus había frente a los ya conocidos Azure WebSites + Azure Web Jobs. La semana pasada se llevó a cabo el Dev Days Latam en la ciudad de Lima, un evento sin presentes en la región al que tuve la oportunidad de asistir, y donde por primera vez escuché este concepto en una de las sesiones.

Decir que son los microservicios me resulta tan difícil como decir que es la programación funcional, si bien puedo decir algunas características no tengo una definición precisa y desconozco si la hay. En una ocasión un colega dijo “son Bounded Contexts as a Service” y es una definición que me gusta, pues de ahí se desprende lo de las pequeñas unidades de despliegue y la posibilidad de usar un lenguaje para cada necesidad y etc. etc. Imaginar que son los Serverless Microservices me hizo pensar en unidades de despliegue que no van a un “server” (?) ¿imágenes de Docker auto gestionadas en un host que yo no controlo? al googlear el termino para ver alguna definición o implementación veo que, para sorpresa mía, los primeros resultados son implementaciones hechas en AWS Lambda. ¿Pero no es lo mismo que un Azure WebSite + Web Job?

Desconozco mucho de la oferta de computo en la nube de Amazon así que traduzco la documentación de AWS Lambda a los Azure Web Sites y Web Jobs, pues me ofrece muchas de las características que allí se muestran. Pero si Azure ya tenía estos dos servicios ¿Para qué crear uno nuevo, si de hecho, lo Functions están basados en el Azure WebJob SDK?

Rescato algunos de los conceptos: Compute on-demand, que incluye un nuevo modelo de cobro y la posibilidad de responder a eventos de servicios de terceros (Http – WebHooks (?)). Y por último, pero no menos importante, el modelo de programación: Un Function App es una carpeta que contiene un archivo host.json y carpetas con las funciones. Estas funciones (carpetas) son archivos de código (en los lenguajes soportados) y un archivo function.json que define la función. La documentación completa está aquí.

Podemos entonces definir un Function App como el siguiente:

MyService
|host.json
|Membershib
|| node_modules
|| function.json
|| index.js
|SayHello
|| function.json
|| project.json
|| run.csx

En la estructura de este ejemplo he dejado dos funciones en lenguajes distintos dentro del mismo Function App (ahora mismo no sé si escala por función o por App), pero se puede mejorar esta implementación teniendo un servicio independiente de membership y uno “por cada necesidad”

Cómo cada Función tiene su propio endpoint… ¿Cómo hacemos para “unificarlo”? Azure tiene el servicio de Api Management que nos viene perfecto para exponerla como un Api, además de otros plus del servicio.

Así como los microservicios voy a dejar este post corto, solo con el concepto y sin más detalles de la implementación. Ya luego si me animo sigo con algo más parecido a un tutorial.

He dejado algo de código, con la estructura del proyecto que mostré aquí, en mi GitHub.

Serverless Microservices con Azure Functions y Api Management

Cómo usar el custom authentication de Azure Mobile Services .NET back-end

Trabajando con ZUMO .NET back-end tuve la necesidad de implementar una autenticación personalizada, es decir, que no sea con proveedores de terceros sino con usuarios y contraseñas que están en x base de datos. Afortunadamente para mi, Microsoft tiene esta documentación, así que no me esforzaré en reescribir lo que allí se explica. Lo que allí no comentan es el lío que hay con las versiones de los paquetes, cómo usar el token desde un cliente ni cómo obtenerla identidad del usuario en el lado del servidor.

TL;DR;

No actualicen la versión del System.IdentityModel.Tokens.Jwt Usar el header “X-ZUMO-AUTH” para pasar el token al servicio.

Líos con las versiones de los paquetes

Una vez creado el proyecto lo primero que hice fue actualizar los paquetes, hace falta agregar unos cuantos assembly binding redirects y cuando todos ya están listos falla con pantalla amarilla y  el error:

Method not found: 'Void System.IdentityModel.Tokens.JwtSecurityTokenHandler.set_CertificateValidator(System.IdentityModel.Selectors.X509CertificateValidator)'. 

Si ya la has cagado como yo, la solución esta en ejecutar dos comandos en el Package Manager Console:

> uninstall-package System.IdentityModel.Tokens.Jwt -Version 3.0.2 -Force
> uninstall-package System.IdentityModel.Tokens.Jwt -Version 4.0.1 -Force
> install-package System.IdentityModel.Tokens.Jwt -Version 3.0.2

En los comentarios de la entrada en inglés dicen que aparentemente la autenticación no se realiza por culpa de la versión de este paquete, pero en lo que probé parece que solo es lío de usar la cabecera correcta.

Cabecera de autenticación

Después de leer algo de la documentación no encontré nada que dijera algo sobre la cabecera X-ZUMO-AUTH y al hacer la búsqueda parece que no hay documentación oficial (Si encuentran algo no duden es comentarlo). La encontré después de leer algunos blogs. En fin, para poder usar el token de autenticación  hay que pasar la cabecera X-ZUMO-AUTH y no el Authorization. El token lo recuperamos haciendo un request a /customlogin/ y este será pasado a las acciones que requieran autenticación.   image   image   image

Obtener el usuario en el back-end

Aunque WAMS es una envoltura al rededor de ASP.NET WEB API y otros servicios, ciertamente hay cosas que cambian y aún no entiendo muy bien, cómo por qué cambia el Type del claim que guarda la identidad del usuario. Basta con hacer algo de debug para encontrarlo y recuperarlo de la forma:

    [AuthorizeLevel(AuthorizationLevel.User)]
    public class HelloController : ApiController
    {
        public IHttpActionResult Get()
        {
            var user = ClaimsPrincipal.Current.FindFirst(claim => claim.Type == "uid").Value;
            if (user.Contains(":")) user = user.Split(':')[1];
            return Ok("Hello! " + user);
        }
    }

Conectando con JavaScript

El proyecto en el que estoy trabajando en este momento requiere que desde un sitio web se invoquen ciertas acciones expuestas en el servicio móvil. Lo que nos pone ahora la tarea de solucionar el lío del cors. Por fortuna para nosotros desde la actualización 1.0.342 se usa  ASP.NET WEB API 2, y entre sus características está el trabajo con CORS, pero por supuesto, encapsuladas. Para usarla solo basta con agregar un nuevo appSetting en el web.config:

 
<add key="MS_CrossDomainOrigins" value="http://mysite.com" />

Con esto ya podemos llamar desde el cliente js:

 
        $.ajax({
            type: 'POST',
            contentType : "application/json",
            url: 'http://localhost:54085/api/customlogin',
            data: JSON.stringify(userModel)
        }).done(function (data) {
            sessionStorage.setItem('supertoken', data.authenticationToken);
            AppViewModels.assignVM.initView();
        }).fail(function() {
            alert("Usuario o contraseña invalidos");
        });

El código del ejemplo esta aquí

Espero les sea de utilidad.

Hasta el próximo post.

Cómo usar el custom authentication de Azure Mobile Services .NET back-end

[How To] NodeJS, Sinatra, NGINX y Azure Worker Role

UPDATE: Ya esta en este repositorio el código de este ejemplo. El orden ha cambiado y el código de los scripts es más fácil de entender ahora.

En el post anterior vimos como correr NGINX dentro de un Azure Node Worker Role, aunque de NodeJS no vimos nada, pues la configuración la dejamos pendiente para una siguiente entrada. Así pues, veamos como correr NodeJS dentro de nginx en nuestro Azure Worker Role.

1- Crear archivo de configuración para nginx

1.1- Nginx usa un archivo de configuración localizado en la ruta /nginx/conf/nginx.conf Este archivo trae una configuración de forma predeterminada, configuración que debemos sobrescribir para poder usar el proxy inverso y llamar, por ejemplo, a nuestro nodejs.

La mejor forma de hacerlo, para no complicarnos mucho, es crear una copia del nginx.conf en la raíz del WorkerRole1 y agregar la siguiente entrada:

image

1.2- Ahora necesitamos que al finalizar la descarga y el unzip del nginx sea reemplazada la configuración predeterminada por nuestro nuevo .conf. Para esto agregamos esta línea Copy-Item "$current\nginx.conf" "$current\nginx-1.6.0\conf\" -force a el archivo download_nginx.ps1 (archivo que vimos en el articulo anterior). El script queda así:

$current = [string] (Get-Location -PSProvider FileSystem)
$webclient = New-Object System.Net.WebClient
$url = "http://nginx.org/download/nginx-1.6.0.zip"
$file = "$current\nginx.zip"
$webclient.DownloadFile($url,$file)

$shell_app=new-object -com shell.application
$zip_file = $shell_app.namespace($file)
$destination = $shell_app.namespace($current)
$destination.Copyhere($zip_file.items())
Remove-Item nginx.zip

Copy-Item "$current\nginx.conf" "$current\nginx-1.6.0\conf\" -force

$firewallFile = "$current\nginx-1.6.0\nginx.exe"
netsh advfirewall firewall add rule name="nginx" dir=in action=allow program="$firewallFile" enable=yes
cd "nginx-1.6.0"
& ".\nginx.exe"

1.3- Si ejecutamos el Emulador de Azure y navegamos al http://localhost/ veremos algo como:

image

Y si apuntamos a /nodeapp veremos algo como:

image

Y en las herramientas de captura web encontraremos:

image

Hasta aquí este post sería muy corto, así que mostraré como correr Sinatra, a la par, dentro del NGINX.

2- Descargar Ruby e instalar Sinatra

2.1- Agregaremos un archivo llamado download_ruby.ps1 a la raíz del WorkerRole1. El código de este archivo será el siguiente:

$current = [string] (Get-Location -PSProvider FileSystem)
$webclient = New-Object System.Net.WebClient
$url = "http://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.0.0-p481.exe"
$file = "$current\ruby.exe"
$webclient.DownloadFile($url,$file)

Invoke-Expression "$file /silent"
Remove-Item ruby.exe
& "$current\install_sinatra.cmd"

El código anterior descargará la versión 2 de Ruby que encontramos en Ruby Installer. Ejecutará el instalador de forma silenciosa, removerá el .exe y finalmente llamará a otro .cmd que será el encargado de instalar Sinatra.

2.2- Agregamos un archivo llamado install_sinatra.cmd en la raíz del WorkerRole1. con el siguiente código:

 
D:\Ruby200\bin\gem.bat install sinatra --no-ri --no-rdoc

NOTA: En mi maquina de desarrollo (como en muchas) los programas se instalan de forma predeterminada en la partición C del disco duro. En las maquinas virtuales de Azure lo hacen en D:\. No olvides cambiar el cmd o agregar alguna validación para que funcione en ambos entornos.

2.3- Agregaremos ahora un archivo main.rb a la raíz del WorkerRole1, este archivo tendrá alguna configuración de Sinatra y será el encargado de responder las solicitudes que hagamos a la ruta “/”. El código de este archivo es:

require 'sinatra'
set :environment, :development
set :port, 1338
get '/' do
  "Hello, my name is Sinatra"
end

2.4- Ahora que tenemos la aplicación de Ruby lista necesitamos iniciarla, para esto agregaremos u nuevo archivo llamado start_ruby.cmd con una sola línea de código:

D:\Ruby200\bin\ruby.exe main.rb

2.5- Con esto solo nos queda invocar los .cmd correspondientes desde el Worker Role. Para esto agregaremos dos Task al Service Definition, una llamando al .cmd encargado de descargar e instalar Ruby y otra para iniciar nuestra app:

      <Task commandLine="setup_ruby.cmd" executionContext="elevated" taskType="background" />
      <Task commandLine="start_ruby.cmd" executionContext="elevated" taskType="background" />

Las entradas en el Service Definition quedan así :

image

3- Editar el nginx.conf

3.1- Para decirle a nginx que corra Sinatra editaremos la entrada location / del nginx.conf que agregamos, queda así:

        location / {            
            proxy_pass   http://127.0.0.1:1338;
            #root   html;
            #index  index.html index.htm;
        }   

3.2- Si ejecutamos ahora el emulador y navegamos a al localhost veremos algo como:

sinatranodeng

Known Issues

1- Se hace necesario agregar enviaroment variables a las tasks que ejecutan la instalación de Ruby y Sinatra. Esto para poder alternar, por ejemplo, donde se busca el ruby.exe en los .cmd.

2- He notado que en ocasiones(a veces si, a veces no) no se ejecutan todos los Scripts al desplegar en la nube. Aún desconozco la razón de este comportamiento. Por ello se hace necesario habilitar la conexión por escritorio remoto al Azure Role para poder terminar la instalación.

3- Si se cambia la entrada del location de Sinatra en el nginx.conf. Sinatra responde con un 404.

Notas finales

1- Cabe anotar que nada de lo mostrado aquí ha sido probado en entornos de producción.

2- Cualquier sugerencia por favor no dude en comentarlo.

3- En los próximos días publicaré el repo en mi GitHub. Revisar el update

Espero les sea de utilidad.

Hasta el próximo post.

[How To] NodeJS, Sinatra, NGINX y Azure Worker Role

[How to] Correr NGINX en un NodeJS Azure Worker Role

No hace mucho empecé a jugar con nginx para correr django y nodejs dentro del mismo server, haciendo uso de su propiedad de proxy inverso. Ésta es solo una de sus características, pero hablar de nginx daría para muchos artículos, y además, hay documentación oficial del producto.

Estas pruebas las venía realizando dentro de mi maquina de desarrollo, nunca he probado o trabajado con nginx en un entorno productivo, así que, para tratar de llevar un poco más allá estas pruebas me decidí a desplegar el nginx dentro de un worker role que me respondiera las solicitudes al nodejs y demás.

Vale aclarar que existen más formas de hacerlo, como haciendo deploy de una imagen de maquina virtual que ya tenga el nginx o creando una maquina virtual de Ubuntu e instalando y configurando el servidor.

1. Crear el NodeJS Azure Cloud Service

Sobre este tema ya se escribió un tutorial completo en el sitio de azure, pero los pongo aquí para que se entienda.

1.1-  Abrimos el Azure Power Shell con privilegios de administrador, ubicamos el directorio en que vamos a trabajar y ejecutamos el comando New-AzureServiceProject testnginx

image

1.2- Ahora, dentro de la carpeta que creó el comando anterior agregamos un Azure Worker Role con el comando Add-azurenodeworkerrole

image

1.3- Ya con esto podemos probar que está funcionando, haciendo uso del comando start-azureemulator y navegando en el browser a la dirección indicada.

2. Descargar e iniciar nginx

2.1- Lo primero que haremos será revisar que es lo que han hecho estos comandos que ejecutamos en el paso uno. Para ello usaré Sublime Text (aunque no hace falta usar editores complejos, con el File explorer y notepad es suficiente)

image

De entrada vemos la carpeta WorkerRole1, ésta contiene un par de .cmd, .js y un .ps1. Para ver como interactúan estos archivos solo hace falta revisar el ServiceDefinition.csdef. En este ubicaremos el Startup y cada una de sus Tasks. Estas tareas llaman a cada uno de los .cmd que ya habíamos visto antes. Y estos .cmd ejecutarán las tareas que tengan programadas, como por ejemplo, descargar nodejs.exe. Viendo esto podemos darnos cuenta por donde es el camino y empezaremos a realizar todas las tareas de scripting que necesitemos.

2.2- Agregaremos una Task al Startup del Role, esta tarea llamará a un archivo setup_nginx.cmd:

image

De hecho, esta será la única modificación que haremos sobre el ServiceDefinition. Queda así:

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="testnginx" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WorkerRole name="WorkerRole1">
    <Startup>
      <Task commandLine="setup_worker.cmd &gt; log.txt" executionContext="elevated">
        <Environment>
          <Variable name="EMULATED">
            <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
          </Variable>
          <Variable name="RUNTIMEID" value="node" />
          <Variable name="RUNTIMEURL" value="" />
        </Environment>
      </Task>
      <Task commandLine="node.cmd .\startup.js" executionContext="elevated" />
      <Task commandLine="setup_nginx.cmd" executionContext="elevated" taskType="background" />
    </Startup>
    <Endpoints>
      <InputEndpoint name="HttpIn" protocol="tcp" port="80" localPort="80"/>
    </Endpoints>
    <Runtime>
      <Environment>
        <Variable name="PORT">
          <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/Endpoints/Endpoint[@name='HttpIn']/@port" />
        </Variable>
        <Variable name="EMULATED">
          <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
        </Variable>
      </Environment>
      <EntryPoint>
        <ProgramEntryPoint commandLine="node.cmd .\server.js" setReadyOnProcessStart="true" />
      </EntryPoint>
    </Runtime>
  </WorkerRole>
</ServiceDefinition>

2.3- En la carpeta WorkerRole1 agregaremos el archivo setup_nginx.cmd con el siguiente código:

@echo off
powershell -c "Set-ExecutionPolicy Unrestricted"
powershell .\download_nginx.ps1

Este código lo único que haremos es llamar al script downloadnginx.ps1

2.4- En la carpeta WorkerRole1 agregamos un archivo llamado download_nginx.ps1 con el siguiente código:

$current = [string] (Get-Location -PSProvider FileSystem)
$webclient = New-Object System.Net.WebClient
$url = "http://nginx.org/download/nginx-1.6.0.zip"
$file = "$current\nginx.zip"
$webclient.DownloadFile($url,$file)

$shell_app=new-object -com shell.application
$zip_file = $shell_app.namespace($file)
$destination = $shell_app.namespace($current)
$destination.Copyhere($zip_file.items())
Remove-Item nginx.zip

$firewallFile = "$current\nginx-1.6.0\nginx.exe"
netsh advfirewall firewall add rule name="nginx" dir=in action=allow program="$firewallFile" enable=yes
cd "nginx-1.6.0"
& ".\nginx.exe"

Este código se divide en tres secciones. La primera crea un webclient apuntando a la dirección de descarga de la ultima versión estable de nginx, la descarga y la deja en la carpeta “current”. La segunda sección hace un unzip de los archivos y borra el .zip. La tercera y ultima sección del script agrega una regla en el firewall y ejecuta el nginx.exe

2.5- Por ahora (aún no vamos a correr nodejs dentro del nginx) cambiaremos el puerto del nodejs al 1337. Esto en el archivo server.js:

image

2.6- Si ejecutamos el comando start-azureemulator y navegamos al localhost veremos algo como:

image

3- Desplegar en la nube

Sobre esto también se ha escrito documentación en el sitio oficial pero la transcribiré aquí para no perder el hilo de la historia.

3.1- Lo primero será agregar el Azure Account con el comando Add-Azureaccount

3.2- Descargamos el publication settings con el comando Get-AzurePublishSettingsFile

3.3- Importamos el archivo que habíamos descargado con el comando Import-AzurePublishSettingsFile "path donde esta el archivo"

3.4- publicamos con el comando Publish-AzureServiceProject -ServiceName nombre_del_servicio_en_la_nube -Location "East US" -Launch

Espero les sea de utilidad.

Hasta el próximo post.

[How to] Correr NGINX en un NodeJS Azure Worker Role

Azure Service Bus Queue Client + Reactive Extensions

Azure Service Bus soporta dos modelos de mensajería asíncrona basados en colas, éstos son: las colas y los tópicos. Las colas ofrecen una comunicación 1:1, es decir, se publica un mensaje en la cola y de los n receptores sólo uno podrá trabajar el mensaje. Por el contrario, los tópicos, responden de una manera más fiel a un modelo pub/sub, y nos permiten una comunicación 1:n, es decir, el mensaje que se publica en un tópico puede ser recibido por uno o varios  subscritores que podrán o no hacer uso de filtros para recibir solo los mensajes que les interesan; esto se logra haciendo uso de un sistema de colas virtuales para cada subscripción, sistema que controla Azure por nosotros.

Cada modelo responde a necesidades distintas y no son excluyentes, es decir, en un mismo namespace podemos estar trabajando con colas y tópicos al mismo tiempo y usarlas para responder a las necesidades de nuestra solución. Aunque, si lo deseamos, podemos agregar a las colas un comportamiento similar al de los tópicos, interactuando con las propiedades de los mensajes y creando filtros para responder de forma distinta a cada uno de los mensajes que encontremos en la cola. Digo similar, pero esto no llega ni a la mitad de los tópicos, pues aquí no estaríamos empleando ningún sistema de colas distinto al provisto por Azure y se sigue compitiendo por recibir los mensajes.

Desde el SDK 2 tenemos la posibilidad de recibir mensajes de una forma event-driven a través del OnMessage, método que encapsula todo el loop infinito que sondea la cola en espera de nuevos mensajes. Con este modelo dirigido por eventos ya podemos imaginarnos el por qué de los Reactive Extensions. Los Reactive Extensions nos permiten crear secuencias observables de distintas formas, así podemos trabajar con estos datos asíncronos de una forma más sencilla y escribiendo un código más declarativo, fácil de leer y mantener. Así que manos a la obra.

Lo primero es crear la secuencia observable a partir del metodo OnMessage, aquí, no sabía bien cual era el mejor enfoque (aún me pierdo con todos los operadores de Rx), así que pregunté a los colegas y terminé con un código de la forma:

            IObservable<BrokeredMessage> observable = Observable.Create<BrokeredMessage>(
                observer =>
                {
                    _queueClient.OnMessage(observer.OnNext, new OnMessageOptions());
                    return Disposable.Empty;
                }).Publish().RefCount();

El código anterior tiene tres partes importantes, lo primero es acudir al método Create (En la sobrecarga con IDisposale) para crear la secuencia observable y devolver un IDisposable. Esta secuencia observable se creará a partir de la implementación de una función “subscriptora” (El primer parámetro del método). Si dejamos hasta ahí y empezamos a vincular subscriptores (más de uno) a la secuencia observable, el método OnAction se ejecutará cuantas veces se agregué un subscriptor, lo que provocaría una excepción con el mensaje: The method ‘OnMessage’ or ‘OnMessageAsync’ has already been called. Lo que nos lleva al segundo punto importante del código, el operador Publish, éste retornará un IConnectableObservale<T>, que nos permitirá compartir la subscripción (y no solo la secuencia observable), si! compartir el OnMessage que registramos en el Create. Finalmente, hacemos uso del RefCount, que nos retorna un IObservable<T> a partir del IConnectableObservable<T> y manejará por nosotros la conexión y desconexión de los subscriptores.

Teniendo “claro” como se genera la secuencia observable solo queda usar los conocimientos de linq para crear nuevas secuencias observables y subscripciones. En este ejemplo separaremos comandos y consultas y ejecutaremos algo (impresión en pantalla) con esta información. Algo de la forma:

            var comandos = observable.Where(
                message =>
                {
                    object obj;
                    return message.Properties.TryGetValue("IsCommand", out obj) && (bool)obj;
                }).Subscribe(
                x => //OnNext
                {
                    Console.ForegroundColor = ConsoleColor.DarkYellow;
                    Console.WriteLine(x.GetBody<string>());
                },
                x => Console.WriteLine(x.Message), //OnError
                () => Console.WriteLine("Complete")); //OnComplete

Y tenemos como salida:

image

El código completo esta en este gist.

Como vemos la implementación con Reactive Extensions nos permite escribir un código más declarativo, un poco más fácil de mantener y de leer, con lo que ya sabemos de Linq.

Espero les sea de utilidad.

Hasta el próximo post

Azure Service Bus Queue Client + Reactive Extensions