web 2.0

Propulser Subversion (SVN) dans les nuages de Windows Azure

Une des fonctionnalités fort sympathique de Windows Azure est qu’on peut y exécuter à peu prés tout ce qu’on peut exécuter sur un serveur Windows, du moment qu’on ne fait pas trop appel à COM ou à la base de registre.
En résumé tout ce que vous pouvez faire tourner sur Windows sous un compte non administrateur, tournera sur Azure. C’est grâce à cela qu’on peut exécuter du Java sur Windows Azure.

Dans cet article je vais vous présenter comment propulser votre serveur subversion dans les “nuages“ grâce à Windows Azure.

SVN qu’est ce que c’est ?

Pour ceux qui ne connaitraient pas Subversion, c’est un système de gestion de versions centralisé, très populaire et open source, sous licence Apache. Si vous ne connaissez pas Subversion je vous invite à lire cet article sur Wikipedia

Les préparatifs

Commencez par vous procurer les binaires d’un serveur subversion pour Windows. Et la vous avez l’embarras du choix http://subversion.apache.org/packages.html pour cet exemple j’avais pris la version 1.5.6 de collabnet.

Pas besoin d’installer le serveur sur votre poste il vous faut juste extraire le dossier bin qui contient svnserve.exe et les dll associées.

Ensuite on va uploader ce dossier dans les blobs de votre storage Azure. Pour cela j’ai utilisé  le cloudberry explorer qui permet d’y naviguer à la manière d’un explorateur de fichier.

Créez un container nommé “svn” et uploadez y le contenu du dossier bin que vous avez extrait.

Le Worker Role

Un Worker Role va récupérer les binaires stockés dans les blobs, les copier sur le local storage de la VM Azure, puis démarrer notre serveur svn.

Pour cela commençons par créer un nouveau Worker Role en utilisant les templates de Visual Studio.

Configuration du Worker Role

Ouvrez le ServiceDefinition.csdef 

Rajouter l’attribut enableNativeCodeExecution au WorkerRole, ainsi le WorkerRole peut exécuter du code non managé.

<WorkerRole name="WorkerRole" enableNativeCodeExecution="true">

A l’intérieur de cette balise insérez le code nécessaire pour déclarer l’utilisation des LocalResource

<LocalResources>
      <LocalStorage name="InstanceDriveCache" cleanOnRoleRecycle="false" sizeInMB="300" />
      <LocalStorage name="SvnFiles" cleanOnRoleRecycle="true" sizeInMB="300" />
</LocalResources>

Le LocalStorage que j’ai nommé SvnFiles sera pour les binaires de Svn.
Et le InstanceDriveCache servira comme cache disque pour mon repository svn.

A l’intérieur de la balise ConfigurationSettings ajouter :

<Setting name="SvnFilesContainerName" />
Cela permettra de déclarer le nom du container dans lequel j’ai stocké les binaires de SVN et
de le changer par la suite sans modifier le code de mon Worker Role
 
Et enfin dans les Endpoints rajouter comme cela un nouveau endpoint : 
<Endpoints>
      <InputEndpoint name="WorkerIn" protocol="tcp" port="3690" />
</Endpoints>

Ainsi nous ajoutons un endpoint vers l’extérieur pour notre Worker Role, le port étant celui du protocole svn.

Ouvrez le Serviceconfiguration.cscfg

et ajouter dans ConfigurationSettings :

<Setting name="SvnFilesContainerName" value="svn" />
pour déclarer le nom du container dans lequel j’ai stocké les binaires de svn
 
Vous pouvez aussi faire cette configuration en utilisant les écrans de propriétés du WorkerRole 
(clic droit, puis propriété sur le workerrole dans le projet de configuration du service)
 

Le Worker Role

Un serveur SVN c’est bien, mais où seront stockées mes repository ?

En fait nous allons utiliser un Windows Azure Drive (ou Xdrive).  Le Windows Azure Drive c’est un disque NTFS mais dans les nuages. Il est stockée sous forme de blob dans le storage Azure et monté dans la VM Azure par le Worker Role.

L’un des gros avantages d’utiliser le Windows Azure Drive est que mes repository SVN ne sont ainsi nullement lié à Azure. Le jour au je veux rapatrier mon serveur SVN et ses repository chez moi, c’est très simple il me suffit de recopier l’image disque (le .vhd) chez moi et de le remonter comme disque local (trois clics de souris sous Windows 7)

Attention par contre en utilisant un Azure Drive on reste limité en termes de montée en charge, puisque on ne peu monter un Azure drive que sur une seule instance de notre worker role en lecture/écriture.
Si on démarre plusieurs instances notre Azure Drive ne sera disponible qu’en lecture sur les autres instances, et par conséquent notre serveur svn aussi.

Donc la montée en charge se limite à augmenter la taille de la VM.

ouvrez le WorkerRole.cs

dans la classe du WorkerRole déclarer à l’intérieur de la classe cet objet :

private Process svnserve;
private string driveLetter;
private CloudDrive myCloudDrive;

L’objet svnserve nous servira à démarrer le processus du serveur SVN. Et le CloudDrive c’est l’objet correspondant au Windows Azure Drive.

 
Dans la méthode OnStart() rajouter :
// Initialize config environment 
            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
            });
InstallAndStartSvn();  

Les premières lignes sont pour initialiser la configuration, puis la  méthode va copier et démarrer le serveur SVN.

Voila le contenu de la  méthode InstallAndStartSvn  :

Les 2 premières lignes vont met permettre d’initialiser l’accès au blob

 
CloudStorageAccount storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
CloudBlobClient blobfoldersClient = storageAccount.CreateCloudBlobClient();

La je récupère le chemin local à la VM de la localresource que j’ai déclaré dans ma configuration

 
String svnroot = RoleEnvironment.GetLocalResource("SvnFiles").RootPath;
 

J’utilise à présent une classe qui va copier le contenu du container “Svn” dans le dossier correspondant à la local resource  (La classe est disponible plus loin dans l’article)

 
StoreFoldersInBlobs.Folder SvnFolder = new StoreFoldersInBlobs.Folder(
    svnroot, blobfoldersClient.GetContainerReference(RoleEnvironment.GetConfigurationSettingValue("SvnFilesContainerName"))
);
SvnFolder.CopyContainerToFolder();

Trace.WriteLine(" Copie du svn effectué :)");

Maintenant je monte mon Azure Drive.

 
MountAzureDrive();
 

Et je démarre mon serveur SVN.

 
int port = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["WorkerIn"].IPEndpoint.Port;
svnserve = new Process()
  {
     StartInfo = new ProcessStartInfo(Path.Combine(svnroot, @"svnserve.exe"), 
" --daemon" + " --root " + driveLetter + " --listen-port " + port)
      {
         UseShellExecute = false,
         WorkingDirectory = svnroot,
         RedirectStandardOutput = true,
         CreateNoWindow = true
      }
  };
svnserve.Start();    
Trace.WriteLine("Serveur Svn démarré");       
}

A présent n‘oubliez pas de rajouter dans la méthode Run du WorkerRole :

svnserve.WaitForExit();
Trace.WriteLine(" svn a fermé");

Sans quoi votre Worker Role redémarrerait tout de suite aprés avoir fini de démarrer, tournant en boucle indéfiniment de l’état Busy à Starting.

Pour en savoir plus sur les Windows Azure Drive je vous recommande cet article de Thomas Conte

Le code suivant va créer s’il n’existe pas et monter l’Azure Drive.  Vous pouvez ainsi soit créer vos repository en local en utilisant le mode d’emploi de l’article de Thomas Conte pour créer votre vhd en local, y créer un repository à l’aide de la commande "svnadmin create” y régler les droits appropriés en modifiant les fichiers de config du repository puis uploader votre vhd sur Azure toujours avec le cloudberry explorer.

Le contenu de la méthode qui va monter le Windows Azure Drive pour le stockage du repository :

 
CloudStorageAccount storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
LocalResource localCache = RoleEnvironment.GetLocalResource("InstanceDriveCache");
CloudDrive.InitializeCache(localCache.RootPath + "cache", localCache.MaximumSizeInMegabytes);
Trace.WriteLine(" cache créé");
// Just checking: make sure the container exists 
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
blobClient.GetContainerReference("drives").CreateIfNotExist();

// Create cloud drive 
myCloudDrive = storageAccount.CreateCloudDrive(
    blobClient
    .GetContainerReference("drives")
    .GetPageBlobReference("mysupercooldrive.vhd")
    .Uri.ToString()
);

try
{
    myCloudDrive.Create(64);
}
catch (CloudDriveException ex)
{
    Trace.WriteLine(ex.Message);
//     handle exception here 
//    exception is also thrown if all is well but the drive already exists 
}
driveLetter = "";
try
{
    driveLetter = myCloudDrive.Mount(25, DriveMountOptions.Force);
}
catch (Exception e)
{
    Trace.WriteLine(e.Message);
}
Trace.WriteLine("disque monté " + driveLetter);

 

Soit tout faire sur le cloud, en écrivant le code approprié. Voila un exemple de code que vous pouvez rajouter dans votre Worker Role pour créer un repository aprés le démarrage du serveur :

Process svnad = new Process()
  {
     StartInfo = new ProcessStartInfo(Path.Combine(svnroot, @"svnadmin.exe"), " create " + driveLetter + "test")
     {
        UseShellExecute = false,
        WorkingDirectory = svnroot,
        RedirectStandardOutput = true,
        CreateNoWindow = true
      }
   };
   svnad.Start;
   svnad.WaitForExit();
   svnad.Dispose();

Vous pouvez aussi créer un Web Role avec une page d’administration qui exécutera ce code, qui permettra ainsi de créer de nouveaux repository.

Et créer d’autres pages web d’administration qui permettront de gérer les droits de vos repository SVN.

La classe qui sert à copier le contenu du container vers la localresource :

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.StorageClient;

namespace StoreFoldersInBlobs
{
    public class Folder
    {
       private string LocalPath;
       private CloudBlobContainer container;

       private string GetLocalPath(string uri)
       {
           string path = LocalPath;
           // skip domain and container
           // note that this would almost certainly break running against dev storage (where paths look different)
           foreach (var segment in new Uri(uri).Segments.Skip(2))
           {
               path = Path.Combine(path, segment);
           }
           return path;
       }
       public Folder(string _localpath,CloudBlobContainer _blobcontainer)
       {
           LocalPath = _localpath;

           container = _blobcontainer;
       }
       public void CopyContainerToFolder()
       {
         var cloudBlobs = container.ListBlobs(new BlobRequestOptions() 
         { UseFlatBlobListing = true, BlobListingDetails = BlobListingDetails.Metadata }).OfType<CloudBlob>();
         foreach (var blob in cloudBlobs)
         {
             var path = GetLocalPath(blob.Uri.ToString());
             Directory.CreateDirectory(Path.GetDirectoryName(path));
             blob.DownloadToFile(GetLocalPath(blob.Uri.ToString()));
         }
       }

    }
}
 

Maintenant vous êtes prêt à propulser votre serveur SVN dans les nuages.

Mais ce n’est qu’une premiére étape, il serait de possible d’y ajouter un serveur de build, de test et de mettre tout votre processus d’intégration continue dans les nuages.

Mais cela c’est une autre histoire. Si vous êtes intéressés par cette idée, que vous êtes intéressés par un serveur SVN dans les nuages ou que vous désirez mettre votre serveur d’intégration continue dans les nuages n’hésitez pas à me contacter, ou à ajouter vos remarques dans les commentaires.

Tags: