mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-12-02 19:44:02 +08:00
Implemented AzureShellSettingsManager
Refactored common used methods into AzureHelper Added unit tests from DefaultSettingsManagerTests class --HG-- branch : dev
This commit is contained in:
62
src/Orchard.Azure.Tests/AzureVirtualEnvironmentTest.cs
Normal file
62
src/Orchard.Azure.Tests/AzureVirtualEnvironmentTest.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System.Configuration;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.WindowsAzure.StorageClient;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Orchard.Azure.Tests {
|
||||||
|
public abstract class AzureVirtualEnvironmentTest {
|
||||||
|
private Process _dsService;
|
||||||
|
|
||||||
|
protected abstract void OnInit();
|
||||||
|
|
||||||
|
[TestFixtureSetUp]
|
||||||
|
public void FixtureSetup() {
|
||||||
|
var count = Process.GetProcessesByName("DSService").Length;
|
||||||
|
if ( count == 0 ) {
|
||||||
|
var start = new ProcessStartInfo {
|
||||||
|
Arguments = "/devstore:start",
|
||||||
|
FileName =
|
||||||
|
Path.Combine(ConfigurationManager.AppSettings["AzureSDK"], @"bin\csrun.exe")
|
||||||
|
};
|
||||||
|
|
||||||
|
_dsService = new Process { StartInfo = start };
|
||||||
|
_dsService.Start();
|
||||||
|
_dsService.WaitForExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
OnInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestFixtureTearDown]
|
||||||
|
public void FixtureTearDown() {
|
||||||
|
|
||||||
|
if ( _dsService != null )
|
||||||
|
_dsService.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DeleteAllBlobs(CloudBlobContainer container) {
|
||||||
|
foreach ( var blob in container.ListBlobs() ) {
|
||||||
|
if ( blob is CloudBlob ) {
|
||||||
|
( (CloudBlob)blob ).DeleteIfExists();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( blob is CloudBlobDirectory ) {
|
||||||
|
DeleteAllBlobs((CloudBlobDirectory)blob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeleteAllBlobs(CloudBlobDirectory cloudBlobDirectory) {
|
||||||
|
foreach ( var blob in cloudBlobDirectory.ListBlobs() ) {
|
||||||
|
if ( blob is CloudBlob ) {
|
||||||
|
( (CloudBlob)blob ).DeleteIfExists();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( blob is CloudBlobDirectory ) {
|
||||||
|
DeleteAllBlobs((CloudBlobDirectory)blob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.WindowsAzure;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Orchard.Azure.Environment.Configuration;
|
||||||
|
using Orchard.Environment.Configuration;
|
||||||
|
|
||||||
|
namespace Orchard.Azure.Tests.Environment.Configuration {
|
||||||
|
[TestFixture]
|
||||||
|
public class AzureShellSettingsManagerTests : AzureVirtualEnvironmentTest {
|
||||||
|
|
||||||
|
protected IShellSettingsManager Loader;
|
||||||
|
|
||||||
|
protected override void OnInit() {
|
||||||
|
CloudStorageAccount devAccount;
|
||||||
|
CloudStorageAccount.TryParse("UseDevelopmentStorage=true", out devAccount);
|
||||||
|
|
||||||
|
Loader = new AzureShellSettingsManager(devAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() {
|
||||||
|
// ensure default container is empty before running any test
|
||||||
|
DeleteAllBlobs( ((AzureShellSettingsManager)Loader).Container);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SingleSettingsFileShouldComeBackAsExpected() {
|
||||||
|
|
||||||
|
Loader.SaveSettings(new ShellSettings { Name = "Default", DataProvider = "SQLite", DataConnectionString = "something else" });
|
||||||
|
|
||||||
|
var settings = Loader.LoadSettings().Single();
|
||||||
|
Assert.That(settings, Is.Not.Null);
|
||||||
|
Assert.That(settings.Name, Is.EqualTo("Default"));
|
||||||
|
Assert.That(settings.DataProvider, Is.EqualTo("SQLite"));
|
||||||
|
Assert.That(settings.DataConnectionString, Is.EqualTo("something else"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void MultipleFilesCanBeDetected() {
|
||||||
|
|
||||||
|
Loader.SaveSettings(new ShellSettings { Name = "Default", DataProvider = "SQLite", DataConnectionString = "something else" });
|
||||||
|
Loader.SaveSettings(new ShellSettings { Name = "Another", DataProvider = "SQLite2", DataConnectionString = "something else2" });
|
||||||
|
|
||||||
|
var settings = Loader.LoadSettings();
|
||||||
|
Assert.That(settings.Count(), Is.EqualTo(2));
|
||||||
|
|
||||||
|
var def = settings.Single(x => x.Name == "Default");
|
||||||
|
Assert.That(def.Name, Is.EqualTo("Default"));
|
||||||
|
Assert.That(def.DataProvider, Is.EqualTo("SQLite"));
|
||||||
|
Assert.That(def.DataConnectionString, Is.EqualTo("something else"));
|
||||||
|
|
||||||
|
var alt = settings.Single(x => x.Name == "Another");
|
||||||
|
Assert.That(alt.Name, Is.EqualTo("Another"));
|
||||||
|
Assert.That(alt.DataProvider, Is.EqualTo("SQLite2"));
|
||||||
|
Assert.That(alt.DataConnectionString, Is.EqualTo("something else2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void NewSettingsCanBeStored() {
|
||||||
|
Loader.SaveSettings(new ShellSettings { Name = "Default", DataProvider = "SQLite", DataConnectionString = "something else" });
|
||||||
|
|
||||||
|
var foo = new ShellSettings { Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux" };
|
||||||
|
|
||||||
|
Assert.That(Loader.LoadSettings().Count(), Is.EqualTo(1));
|
||||||
|
Loader.SaveSettings(foo);
|
||||||
|
Assert.That(Loader.LoadSettings().Count(), Is.EqualTo(2));
|
||||||
|
|
||||||
|
var text = ( (AzureShellSettingsManager)Loader ).Container.GetBlockBlobReference("Foo/Settings.txt").DownloadText();
|
||||||
|
Assert.That(text, Is.StringContaining("Foo"));
|
||||||
|
Assert.That(text, Is.StringContaining("Bar"));
|
||||||
|
Assert.That(text, Is.StringContaining("Quux"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,6 +53,8 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="AzureVirtualEnvironmentTest.cs" />
|
||||||
|
<Compile Include="Environment\Configuration\AzureShellSettingsManagerTests.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Storage\AzureBlobStorageProviderTests.cs" />
|
<Compile Include="Storage\AzureBlobStorageProviderTests.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -11,74 +11,21 @@ using System.Text;
|
|||||||
|
|
||||||
namespace Orchard.Azure.Tests.Storage {
|
namespace Orchard.Azure.Tests.Storage {
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class AzureBlobStorageProviderTests {
|
public class AzureBlobStorageProviderTests : AzureVirtualEnvironmentTest {
|
||||||
|
|
||||||
#region Azure Environment initialization
|
private AzureBlobStorageProvider _azureBlobStorageProvider;
|
||||||
|
|
||||||
private Process _dsService;
|
|
||||||
|
|
||||||
[TestFixtureSetUp]
|
|
||||||
public void FixtureSetup() {
|
|
||||||
var count = Process.GetProcessesByName("DSService").Length;
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
var start = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
Arguments = "/devstore:start",
|
|
||||||
FileName =
|
|
||||||
Path.Combine(ConfigurationManager.AppSettings["AzureSDK"], @"bin\csrun.exe")
|
|
||||||
};
|
|
||||||
|
|
||||||
_dsService = new Process { StartInfo = start };
|
|
||||||
_dsService.Start();
|
|
||||||
_dsService.WaitForExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
protected override void OnInit() {
|
||||||
CloudStorageAccount devAccount;
|
CloudStorageAccount devAccount;
|
||||||
CloudStorageAccount.TryParse("UseDevelopmentStorage=true", out devAccount);
|
CloudStorageAccount.TryParse("UseDevelopmentStorage=true", out devAccount);
|
||||||
|
|
||||||
_azureBlobStorageProvider = new AzureBlobStorageProvider("default", devAccount);
|
_azureBlobStorageProvider = new AzureBlobStorageProvider("default", devAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestFixtureTearDown]
|
|
||||||
public void FixtureTearDown() {
|
|
||||||
|
|
||||||
if(_dsService != null)
|
|
||||||
_dsService.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup() {
|
public void Setup() {
|
||||||
// ensure default container is empty before running any test
|
// ensure default container is empty before running any test
|
||||||
DeleteAllBlobs();
|
DeleteAllBlobs(_azureBlobStorageProvider.Container);
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private AzureBlobStorageProvider _azureBlobStorageProvider;
|
|
||||||
|
|
||||||
private void DeleteAllBlobs() {
|
|
||||||
foreach(var blob in _azureBlobStorageProvider.Container.ListBlobs()) {
|
|
||||||
if(blob is CloudBlob) {
|
|
||||||
((CloudBlob) blob).DeleteIfExists();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blob is CloudBlobDirectory) {
|
|
||||||
DeleteAllBlobs((CloudBlobDirectory)blob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeleteAllBlobs(CloudBlobDirectory cloudBlobDirectory) {
|
|
||||||
foreach (var blob in cloudBlobDirectory.ListBlobs()) {
|
|
||||||
if (blob is CloudBlob) {
|
|
||||||
((CloudBlob)blob).DeleteIfExists();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blob is CloudBlobDirectory) {
|
|
||||||
DeleteAllBlobs((CloudBlobDirectory)blob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@@ -131,8 +78,7 @@ namespace Orchard.Azure.Tests.Storage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void CreateFileShouldBeFolderAgnostic()
|
public void CreateFileShouldBeFolderAgnostic() {
|
||||||
{
|
|
||||||
_azureBlobStorageProvider.CreateFile("foo.txt");
|
_azureBlobStorageProvider.CreateFile("foo.txt");
|
||||||
_azureBlobStorageProvider.CreateFile("folder/foo.txt");
|
_azureBlobStorageProvider.CreateFile("folder/foo.txt");
|
||||||
_azureBlobStorageProvider.CreateFile("folder/folder/foo.txt");
|
_azureBlobStorageProvider.CreateFile("folder/folder/foo.txt");
|
||||||
@@ -193,9 +139,9 @@ namespace Orchard.Azure.Tests.Storage {
|
|||||||
|
|
||||||
var foo = _azureBlobStorageProvider.CreateFile("folder1/foo.txt");
|
var foo = _azureBlobStorageProvider.CreateFile("folder1/foo.txt");
|
||||||
|
|
||||||
using(var stream = foo.OpenWrite())
|
using ( var stream = foo.OpenWrite() )
|
||||||
using (var writer = new StreamWriter(stream))
|
using ( var writer = new StreamWriter(stream) )
|
||||||
writer.Write(teststring);
|
writer.Write(teststring);
|
||||||
|
|
||||||
Assert.AreEqual(22, foo.GetSize());
|
Assert.AreEqual(22, foo.GetSize());
|
||||||
|
|
||||||
|
|||||||
82
src/Orchard.Azure/AzureHelper.cs
Normal file
82
src/Orchard.Azure/AzureHelper.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.WindowsAzure.StorageClient;
|
||||||
|
|
||||||
|
namespace Orchard.Azure {
|
||||||
|
public class AzureHelper {
|
||||||
|
|
||||||
|
public static bool BlobExists(CloudBlobContainer container, string path) {
|
||||||
|
if ( String.IsNullOrEmpty(path) || path.Trim() == String.Empty )
|
||||||
|
throw new ArgumentException("Path can't be empty");
|
||||||
|
|
||||||
|
try {
|
||||||
|
var blob = container.GetBlockBlobReference(path);
|
||||||
|
blob.FetchAttributes();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch ( StorageClientException e ) {
|
||||||
|
if ( e.ErrorCode == StorageErrorCode.ResourceNotFound ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnsurePathIsRelative(string path) {
|
||||||
|
if ( path.StartsWith("/") )
|
||||||
|
throw new ArgumentException("Path must be relative");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnsureBlobExists(CloudBlobContainer container, string path) {
|
||||||
|
if ( !BlobExists(container, path) ) {
|
||||||
|
throw new ArgumentException("File " + path + " does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnsureBlobDoesNotExist(CloudBlobContainer container, string path) {
|
||||||
|
if ( BlobExists(container, path) ) {
|
||||||
|
throw new ArgumentException("File " + path + " already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool DirectoryExists(CloudBlobContainer container, string path) {
|
||||||
|
if ( String.IsNullOrEmpty(path) || path.Trim() == String.Empty )
|
||||||
|
throw new ArgumentException("Path can't be empty");
|
||||||
|
|
||||||
|
return container.GetDirectoryReference(path).ListBlobs().Count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnsureDirectoryExists(CloudBlobContainer container, string path) {
|
||||||
|
if ( !DirectoryExists(container, path) ) {
|
||||||
|
throw new ArgumentException("Directory " + path + " does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnsureDirectoryDoesNotExist(CloudBlobContainer container, string path) {
|
||||||
|
if ( DirectoryExists(container, path) ) {
|
||||||
|
throw new ArgumentException("Directory " + path + " already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Combine(string path1, string path2) {
|
||||||
|
EnsurePathIsRelative(path1);
|
||||||
|
EnsurePathIsRelative(path2);
|
||||||
|
|
||||||
|
if ( path1 == null || path2 == null )
|
||||||
|
throw new ArgumentException("One or more path is null");
|
||||||
|
|
||||||
|
if ( path1.Trim() == String.Empty )
|
||||||
|
return path2;
|
||||||
|
|
||||||
|
if ( path2.Trim() == String.Empty )
|
||||||
|
return path1;
|
||||||
|
|
||||||
|
var uri1 = new Uri(path1);
|
||||||
|
var uri2 = new Uri(path2);
|
||||||
|
|
||||||
|
return uri2.IsAbsoluteUri ? uri2.ToString() : new Uri(uri1, uri2).ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Yaml.Serialization;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Microsoft.WindowsAzure.StorageClient;
|
||||||
|
using Orchard.Localization;
|
||||||
|
using Orchard.Environment.Configuration;
|
||||||
|
using Microsoft.WindowsAzure;
|
||||||
|
|
||||||
|
namespace Orchard.Azure.Environment.Configuration {
|
||||||
|
|
||||||
|
public class AzureShellSettingsManager : IShellSettingsManager {
|
||||||
|
public const string ContainerName = "sites"; // container names must be lower cased
|
||||||
|
|
||||||
|
private readonly CloudStorageAccount _storageAccount;
|
||||||
|
public CloudBlobClient BlobClient { get; private set; }
|
||||||
|
public CloudBlobContainer Container { get; private set; }
|
||||||
|
|
||||||
|
Localizer T { get; [UsedImplicitly]
|
||||||
|
set; }
|
||||||
|
|
||||||
|
public AzureShellSettingsManager() : this(CloudStorageAccount.FromConfigurationSetting("DataConnectionString"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public AzureShellSettingsManager(CloudStorageAccount storageAccount)
|
||||||
|
{
|
||||||
|
// Setup the connection to custom storage accountm, e.g. Development Storage
|
||||||
|
_storageAccount = storageAccount;
|
||||||
|
|
||||||
|
BlobClient = _storageAccount.CreateCloudBlobClient();
|
||||||
|
|
||||||
|
// Get and create the container if it does not exist
|
||||||
|
// The container is named with DNS naming restrictions (i.e. all lower case)
|
||||||
|
Container = new CloudBlobContainer(ContainerName, BlobClient);
|
||||||
|
Container.CreateIfNotExist();
|
||||||
|
|
||||||
|
// Tenant settings are protected by default
|
||||||
|
Container.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Off });
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<ShellSettings> IShellSettingsManager.LoadSettings() {
|
||||||
|
return LoadSettings().ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IShellSettingsManager.SaveSettings(ShellSettings settings) {
|
||||||
|
if ( settings == null )
|
||||||
|
throw new ArgumentException(T("There are no settings to save.").ToString());
|
||||||
|
if ( string.IsNullOrEmpty(settings.Name) )
|
||||||
|
throw new ArgumentException(T("Settings \"Name\" is not set.").ToString());
|
||||||
|
|
||||||
|
var filePath =String.Concat(settings.Name, "/", "Settings.txt");
|
||||||
|
var blob = Container.GetBlockBlobReference(filePath);
|
||||||
|
blob.UploadText(ComposeSettings(settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<ShellSettings> LoadSettings() {
|
||||||
|
var settingsBlobs =
|
||||||
|
BlobClient.ListBlobsWithPrefix(Container.Name + "/" ).OfType<CloudBlobDirectory>()
|
||||||
|
.SelectMany(directory => directory.ListBlobs()).OfType<CloudBlockBlob>()
|
||||||
|
.Where(blob => string.Equals(Path.GetFileName(blob.Uri.ToString()), "Settings.txt", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
foreach ( var settingsBlob in settingsBlobs ) {
|
||||||
|
yield return ParseSettings(settingsBlob.DownloadText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Content {
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string DataProvider { get; set; }
|
||||||
|
public string DataConnectionString { get; set; }
|
||||||
|
public string DataPrefix { get; set; }
|
||||||
|
public string RequestUrlHost { get; set; }
|
||||||
|
public string RequestUrlPrefix { get; set; }
|
||||||
|
public string State { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static ShellSettings ParseSettings(string text) {
|
||||||
|
var ser = new YamlSerializer();
|
||||||
|
var content = ser.Deserialize(text, typeof(Content)).Cast<Content>().Single();
|
||||||
|
return new ShellSettings {
|
||||||
|
Name = content.Name,
|
||||||
|
DataProvider = content.DataProvider,
|
||||||
|
DataConnectionString = content.DataConnectionString,
|
||||||
|
DataTablePrefix = content.DataPrefix,
|
||||||
|
RequestUrlHost = content.RequestUrlHost,
|
||||||
|
RequestUrlPrefix = content.RequestUrlPrefix,
|
||||||
|
State = new TenantState(content.State)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static string ComposeSettings(ShellSettings settings) {
|
||||||
|
if ( settings == null )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var ser = new YamlSerializer();
|
||||||
|
return ser.Serialize(new Content {
|
||||||
|
Name = settings.Name,
|
||||||
|
DataProvider = settings.DataProvider,
|
||||||
|
DataConnectionString = settings.DataConnectionString,
|
||||||
|
DataPrefix = settings.DataTablePrefix,
|
||||||
|
RequestUrlHost = settings.RequestUrlHost,
|
||||||
|
RequestUrlPrefix = settings.RequestUrlPrefix,
|
||||||
|
State = settings.State != null ? settings.State.ToString() : String.Empty
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,8 +44,14 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="YamlSerializer, Version=0.9.0.2, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\..\lib\yaml\YamlSerializer.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="AzureHelper.cs" />
|
||||||
|
<Compile Include="Environment\Configuration\AzureShellSettingsManager.cs" />
|
||||||
<Compile Include="Storage\AzureBlobStorageProvider.cs" />
|
<Compile Include="Storage\AzureBlobStorageProvider.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -6,22 +6,19 @@ using Microsoft.WindowsAzure.StorageClient;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Orchard.Storage;
|
using Orchard.Storage;
|
||||||
|
|
||||||
namespace Orchard.Azure.Storage
|
namespace Orchard.Azure.Storage {
|
||||||
{
|
public interface IDependency { }
|
||||||
public interface IDependency {}
|
|
||||||
|
|
||||||
public class AzureBlobStorageProvider : IStorageProvider
|
public class AzureBlobStorageProvider : IStorageProvider {
|
||||||
{
|
|
||||||
private readonly CloudStorageAccount _storageAccount;
|
private readonly CloudStorageAccount _storageAccount;
|
||||||
public CloudBlobClient BlobClient { get; private set; }
|
public CloudBlobClient BlobClient { get; private set; }
|
||||||
public CloudBlobContainer Container { get; private set; }
|
public CloudBlobContainer Container { get; private set; }
|
||||||
|
|
||||||
public AzureBlobStorageProvider(string containerName) : this(containerName, CloudStorageAccount.FromConfigurationSetting("DataConnectionString"))
|
public AzureBlobStorageProvider(string containerName)
|
||||||
{
|
: this(containerName, CloudStorageAccount.FromConfigurationSetting("DataConnectionString")) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AzureBlobStorageProvider(string containerName, CloudStorageAccount storageAccount)
|
public AzureBlobStorageProvider(string containerName, CloudStorageAccount storageAccount) {
|
||||||
{
|
|
||||||
// Setup the connection to custom storage accountm, e.g. Development Storage
|
// Setup the connection to custom storage accountm, e.g. Development Storage
|
||||||
_storageAccount = storageAccount;
|
_storageAccount = storageAccount;
|
||||||
|
|
||||||
@@ -29,102 +26,36 @@ namespace Orchard.Azure.Storage
|
|||||||
// Get and create the container if it does not exist
|
// Get and create the container if it does not exist
|
||||||
// The container is named with DNS naming restrictions (i.e. all lower case)
|
// The container is named with DNS naming restrictions (i.e. all lower case)
|
||||||
Container = BlobClient.GetContainerReference(containerName);
|
Container = BlobClient.GetContainerReference(containerName);
|
||||||
|
Container.CreateIfNotExist();
|
||||||
|
|
||||||
Container.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Container });
|
Container.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Container });
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EnsurePathIsRelative(string path) {
|
|
||||||
if(path.StartsWith("/"))
|
|
||||||
throw new ArgumentException("Path must be relative");
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetPrefix(string path) {
|
|
||||||
var prefix = String.Concat(Container.Name, "/", path);
|
|
||||||
if (prefix.EndsWith("/"))
|
|
||||||
return prefix;
|
|
||||||
|
|
||||||
return String.Concat(prefix, "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool BlobExists(string path) {
|
|
||||||
if(String.IsNullOrEmpty(path) || path.Trim() == String.Empty)
|
|
||||||
throw new ArgumentException("Path can't be empty");
|
|
||||||
|
|
||||||
try {
|
|
||||||
var blob = Container.GetBlockBlobReference(path);
|
|
||||||
blob.FetchAttributes();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (StorageClientException e) {
|
|
||||||
if (e.ErrorCode == StorageErrorCode.ResourceNotFound) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureBlobExists(string path)
|
|
||||||
{
|
|
||||||
if (!BlobExists(path)) {
|
|
||||||
throw new ArgumentException("File " + path + " does not exist");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureBlobDoesNotExist(string path)
|
|
||||||
{
|
|
||||||
if (BlobExists(path)) {
|
|
||||||
throw new ArgumentException("File " + path + " already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool DirectoryExists(string path)
|
|
||||||
{
|
|
||||||
if (String.IsNullOrEmpty(path) || path.Trim() == String.Empty)
|
|
||||||
throw new ArgumentException("Path can't be empty");
|
|
||||||
|
|
||||||
return Container.GetDirectoryReference(path).ListBlobs().Count() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureDirectoryExists(string path)
|
|
||||||
{
|
|
||||||
if (!DirectoryExists(path)) {
|
|
||||||
throw new ArgumentException("Directory " + path + " does not exist");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureDirectoryDoesNotExist(string path)
|
|
||||||
{
|
|
||||||
if (DirectoryExists(path)) {
|
|
||||||
throw new ArgumentException("Directory " + path + " already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region IStorageProvider Members
|
|
||||||
|
|
||||||
public IStorageFile GetFile(string path) {
|
public IStorageFile GetFile(string path) {
|
||||||
EnsurePathIsRelative(path);
|
AzureHelper.EnsurePathIsRelative(path);
|
||||||
EnsureBlobExists(path);
|
AzureHelper.EnsureBlobExists(Container, path);
|
||||||
return new AzureBlobFileStorage(Container.GetBlockBlobReference(path));
|
return new AzureBlobFileStorage(Container.GetBlockBlobReference(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IStorageFile> ListFiles(string path)
|
public IEnumerable<IStorageFile> ListFiles(string path) {
|
||||||
{
|
AzureHelper.EnsurePathIsRelative(path);
|
||||||
EnsurePathIsRelative(path);
|
|
||||||
foreach (var blobItem in BlobClient.ListBlobsWithPrefix(GetPrefix(path)).OfType<CloudBlockBlob>()) {
|
string prefix = String.Concat(Container.Name, "/", path);
|
||||||
|
if ( !prefix.EndsWith("/") )
|
||||||
|
prefix += "/";
|
||||||
|
|
||||||
|
foreach ( var blobItem in BlobClient.ListBlobsWithPrefix(prefix).OfType<CloudBlockBlob>() ) {
|
||||||
yield return new AzureBlobFileStorage(blobItem);
|
yield return new AzureBlobFileStorage(blobItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IStorageFolder> ListFolders(string path)
|
public IEnumerable<IStorageFolder> ListFolders(string path) {
|
||||||
{
|
AzureHelper.EnsurePathIsRelative(path);
|
||||||
EnsurePathIsRelative(path);
|
if ( !AzureHelper.DirectoryExists(Container, path) ) {
|
||||||
if (!DirectoryExists(path))
|
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
CreateFolder(path);
|
CreateFolder(path);
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch ( Exception ex ) {
|
||||||
throw new ArgumentException(string.Format("The folder could not be created at path: {0}. {1}", path, ex));
|
throw new ArgumentException(string.Format("The folder could not be created at path: {0}. {1}", path, ex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,28 +65,28 @@ namespace Orchard.Azure.Storage
|
|||||||
.OfType<CloudBlobDirectory>()
|
.OfType<CloudBlobDirectory>()
|
||||||
.Select<CloudBlobDirectory, IStorageFolder>(d => new AzureBlobFolderStorage(d))
|
.Select<CloudBlobDirectory, IStorageFolder>(d => new AzureBlobFolderStorage(d))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateFolder(string path) {
|
public void CreateFolder(string path) {
|
||||||
EnsurePathIsRelative(path);
|
AzureHelper.EnsurePathIsRelative(path);
|
||||||
EnsureDirectoryDoesNotExist(path);
|
AzureHelper.EnsureDirectoryDoesNotExist(Container, path);
|
||||||
Container.GetDirectoryReference(path);
|
Container.GetDirectoryReference(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteFolder(string path) {
|
public void DeleteFolder(string path) {
|
||||||
EnsureDirectoryExists(path);
|
AzureHelper.EnsureDirectoryExists(Container, path);
|
||||||
foreach (var blob in Container.GetDirectoryReference(path).ListBlobs()) {
|
foreach ( var blob in Container.GetDirectoryReference(path).ListBlobs() ) {
|
||||||
if (blob is CloudBlob)
|
if ( blob is CloudBlob )
|
||||||
((CloudBlob)blob).Delete();
|
( (CloudBlob)blob ).Delete();
|
||||||
|
|
||||||
if (blob is CloudBlobDirectory)
|
if ( blob is CloudBlobDirectory )
|
||||||
DeleteFolder(blob.Uri.ToString());
|
DeleteFolder(blob.Uri.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RenameFolder(string path, string newPath) {
|
public void RenameFolder(string path, string newPath) {
|
||||||
EnsurePathIsRelative(path);
|
AzureHelper.EnsurePathIsRelative(path);
|
||||||
EnsurePathIsRelative(newPath);
|
AzureHelper.EnsurePathIsRelative(newPath);
|
||||||
|
|
||||||
if ( !path.EndsWith("/") )
|
if ( !path.EndsWith("/") )
|
||||||
path += "/";
|
path += "/";
|
||||||
@@ -182,17 +113,17 @@ namespace Orchard.Azure.Storage
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteFile(string path) {
|
public void DeleteFile(string path) {
|
||||||
EnsurePathIsRelative(path);
|
AzureHelper.EnsurePathIsRelative(path);
|
||||||
EnsureBlobExists(path);
|
AzureHelper.EnsureBlobExists(Container, path);
|
||||||
var blob = Container.GetBlockBlobReference(path);
|
var blob = Container.GetBlockBlobReference(path);
|
||||||
blob.Delete();
|
blob.Delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RenameFile(string path, string newPath) {
|
public void RenameFile(string path, string newPath) {
|
||||||
EnsurePathIsRelative(path);
|
AzureHelper.EnsurePathIsRelative(path);
|
||||||
EnsurePathIsRelative(newPath);
|
AzureHelper.EnsurePathIsRelative(newPath);
|
||||||
EnsureBlobExists(path);
|
AzureHelper.EnsureBlobExists(Container, path);
|
||||||
EnsureBlobDoesNotExist(newPath);
|
AzureHelper.EnsureBlobDoesNotExist(Container, newPath);
|
||||||
|
|
||||||
var blob = Container.GetBlockBlobReference(path);
|
var blob = Container.GetBlockBlobReference(path);
|
||||||
var newBlob = Container.GetBlockBlobReference(newPath);
|
var newBlob = Container.GetBlockBlobReference(newPath);
|
||||||
@@ -201,8 +132,8 @@ namespace Orchard.Azure.Storage
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IStorageFile CreateFile(string path) {
|
public IStorageFile CreateFile(string path) {
|
||||||
EnsurePathIsRelative(path);
|
AzureHelper.EnsurePathIsRelative(path);
|
||||||
if (BlobExists(path)) {
|
if ( AzureHelper.BlobExists(Container, path) ) {
|
||||||
throw new ArgumentException("File " + path + " already exists");
|
throw new ArgumentException("File " + path + " already exists");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,27 +142,6 @@ namespace Orchard.Azure.Storage
|
|||||||
return new AzureBlobFileStorage(blob);
|
return new AzureBlobFileStorage(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Combine(string path1, string path2) {
|
|
||||||
EnsurePathIsRelative(path1);
|
|
||||||
EnsurePathIsRelative(path2);
|
|
||||||
|
|
||||||
if (path1 == null || path2 == null)
|
|
||||||
throw new ArgumentException("One or more path is null");
|
|
||||||
|
|
||||||
if (path1.Trim() == String.Empty)
|
|
||||||
return path2;
|
|
||||||
|
|
||||||
if (path2.Trim() == String.Empty)
|
|
||||||
return path1;
|
|
||||||
|
|
||||||
var uri1 = new Uri(path1);
|
|
||||||
var uri2 = new Uri(path2);
|
|
||||||
|
|
||||||
return uri2.IsAbsoluteUri ? uri2.ToString() : new Uri(uri1, uri2).ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private class AzureBlobFileStorage : IStorageFile {
|
private class AzureBlobFileStorage : IStorageFile {
|
||||||
private readonly CloudBlockBlob _blob;
|
private readonly CloudBlockBlob _blob;
|
||||||
|
|
||||||
@@ -276,8 +186,6 @@ namespace Orchard.Azure.Storage
|
|||||||
_blob = blob;
|
_blob = blob;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IStorageFolder Members
|
|
||||||
|
|
||||||
public string GetName() {
|
public string GetName() {
|
||||||
return _blob.Uri.ToString();
|
return _blob.Uri.ToString();
|
||||||
}
|
}
|
||||||
@@ -291,7 +199,7 @@ namespace Orchard.Azure.Storage
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IStorageFolder GetParent() {
|
public IStorageFolder GetParent() {
|
||||||
if (_blob.Parent != null) {
|
if ( _blob.Parent != null ) {
|
||||||
return new AzureBlobFolderStorage(_blob.Parent);
|
return new AzureBlobFolderStorage(_blob.Parent);
|
||||||
}
|
}
|
||||||
throw new ArgumentException("Directory " + _blob.Uri + " does not have a parent directory");
|
throw new ArgumentException("Directory " + _blob.Uri + " does not have a parent directory");
|
||||||
@@ -300,18 +208,25 @@ namespace Orchard.Azure.Storage
|
|||||||
private static long GetDirectorySize(CloudBlobDirectory directoryBlob) {
|
private static long GetDirectorySize(CloudBlobDirectory directoryBlob) {
|
||||||
long size = 0;
|
long size = 0;
|
||||||
|
|
||||||
foreach (var blobItem in directoryBlob.ListBlobs()) {
|
foreach ( var blobItem in directoryBlob.ListBlobs() ) {
|
||||||
if (blobItem is CloudBlob)
|
if ( blobItem is CloudBlob )
|
||||||
size += ((CloudBlob)blobItem).Properties.Length;
|
size += ( (CloudBlob)blobItem ).Properties.Length;
|
||||||
|
|
||||||
if (blobItem is CloudBlobDirectory)
|
if ( blobItem is CloudBlobDirectory )
|
||||||
size += GetDirectorySize((CloudBlobDirectory)blobItem);
|
size += GetDirectorySize((CloudBlobDirectory)blobItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region IStorageProvider Members
|
||||||
|
|
||||||
|
|
||||||
|
public string Combine(string path1, string path2) {
|
||||||
|
return AzureHelper.Combine(path1, path2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user