-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(MigrationManager): migrating to repo versions
- Loading branch information
1 parent
f133804
commit 3e43d26
Showing
4 changed files
with
314 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Ipfs.Engine.Migration | ||
{ | ||
/// <summary> | ||
/// Provides a migration path to the repository. | ||
/// </summary> | ||
public interface IMigration | ||
{ | ||
/// <summary> | ||
/// The repository version that is created. | ||
/// </summary> | ||
int Version { get; } | ||
|
||
/// <summary> | ||
/// Indicates that an upgrade can be performed. | ||
/// </summary> | ||
bool CanUpgrade { get; } | ||
|
||
/// <summary> | ||
/// Indicates that an downgrade can be performed. | ||
/// </summary> | ||
bool CanDowngrade { get; } | ||
|
||
/// <summary> | ||
/// Upgrade the repository. | ||
/// </summary> | ||
/// <param name="ipfs"> | ||
/// The IPFS system to upgrade. | ||
/// </param> | ||
/// <param name="cancel"></param> | ||
/// <returns></returns> | ||
Task UpgradeAsync(IpfsEngine ipfs, CancellationToken cancel = default(CancellationToken)); | ||
|
||
/// <summary> | ||
/// Downgrade the repository. | ||
/// </summary> | ||
/// <param name="ipfs"> | ||
/// The IPFS system to downgrade. | ||
/// </param> | ||
/// <param name="cancel"></param> | ||
/// <returns></returns> | ||
Task DowngradeAsync(IpfsEngine ipfs, CancellationToken cancel = default(CancellationToken)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Ipfs.Engine.Migration | ||
{ | ||
class MigrateTo1 : IMigration | ||
{ | ||
class Pin1 | ||
{ | ||
public Cid Id; | ||
} | ||
|
||
public int Version => 1; | ||
|
||
public bool CanUpgrade => true; | ||
|
||
public bool CanDowngrade => true; | ||
|
||
public async Task DowngradeAsync(IpfsEngine ipfs, CancellationToken cancel = default(CancellationToken)) | ||
{ | ||
var path = Path.Combine(ipfs.Options.Repository.Folder, "pins"); | ||
var folder = new DirectoryInfo(path); | ||
if (!folder.Exists) | ||
{ | ||
return; | ||
} | ||
|
||
var store = new FileStore<Cid, Pin1> | ||
{ | ||
Folder = path, | ||
NameToKey = (cid) => cid.Hash.ToBase32(), | ||
KeyToName = (key) => new MultiHash(key.FromBase32()) | ||
}; | ||
|
||
var files = folder.EnumerateFiles() | ||
.Where(fi => fi.Length != 0); | ||
foreach (var fi in files) | ||
{ | ||
try | ||
{ | ||
var name = store.KeyToName(fi.Name); | ||
var pin = await store.GetAsync(name, cancel).ConfigureAwait(false); | ||
File.Create(Path.Combine(store.Folder, pin.Id)); | ||
File.Delete(store.GetPath(name)); | ||
} | ||
catch | ||
{ | ||
|
||
} | ||
} | ||
} | ||
|
||
public async Task UpgradeAsync(IpfsEngine ipfs, CancellationToken cancel = default(CancellationToken)) | ||
{ | ||
var path = Path.Combine(ipfs.Options.Repository.Folder, "pins"); | ||
var folder = new DirectoryInfo(path); | ||
if (!folder.Exists) | ||
{ | ||
return; | ||
} | ||
|
||
var store = new FileStore<Cid, Pin1> | ||
{ | ||
Folder = path, | ||
NameToKey = (cid) => cid.Hash.ToBase32(), | ||
KeyToName = (key) => new MultiHash(key.FromBase32()) | ||
}; | ||
|
||
var files = folder.EnumerateFiles() | ||
.Where(fi => fi.Length == 0); | ||
foreach (var fi in files) | ||
{ | ||
try | ||
{ | ||
var cid = Cid.Decode(fi.Name); | ||
await store.PutAsync(cid, new Pin1 { Id = cid }, cancel).ConfigureAwait(false); | ||
File.Delete(fi.FullName); | ||
} | ||
catch | ||
{ | ||
|
||
} | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
using Common.Logging; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Ipfs.Engine.Migration | ||
{ | ||
/// <summary> | ||
/// Allows migration of the repository. | ||
/// </summary> | ||
public class MigrationManager | ||
{ | ||
static ILog log = LogManager.GetLogger(typeof(MigrationManager)); | ||
|
||
readonly IpfsEngine ipfs; | ||
|
||
/// <summary> | ||
/// Creates a new instance of the <see cref="MigrationManager"/> class | ||
/// for the specifed <see cref="IpfsEngine"/>. | ||
/// </summary> | ||
public MigrationManager(IpfsEngine ipfs) | ||
{ | ||
this.ipfs = ipfs; | ||
|
||
Migrations = typeof(MigrationManager).Assembly | ||
.GetTypes() | ||
.Where(x => typeof(IMigration).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract) | ||
.Select(x => (IMigration)Activator.CreateInstance(x)) | ||
.OrderBy(x => x.Version) | ||
.ToList(); | ||
} | ||
|
||
/// <summary> | ||
/// The list of migrations that can be performed. | ||
/// </summary> | ||
public List<IMigration> Migrations { get; private set; } | ||
|
||
/// <summary> | ||
/// Gets the latest supported version number of a repository. | ||
/// </summary> | ||
public int LatestVersion => Migrations.Last().Version; | ||
|
||
/// <summary> | ||
/// Gets the current vesion number of the repository. | ||
/// </summary> | ||
public int CurrentVersion | ||
{ | ||
get | ||
{ | ||
var path = VersionPath(); | ||
if (File.Exists(path)) | ||
{ | ||
using (var reader = new StreamReader(path)) | ||
{ | ||
var s = reader.ReadLine(); | ||
return int.Parse(s, CultureInfo.InvariantCulture); | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
private set | ||
{ | ||
File.WriteAllText(VersionPath(), value.ToString(CultureInfo.InvariantCulture)); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Upgrade/downgrade to the specified version. | ||
/// </summary> | ||
/// <param name="version"> | ||
/// The required version of the repository. | ||
/// </param> | ||
/// <param name="cancel"> | ||
/// </param> | ||
/// <returns></returns> | ||
public async Task MirgrateToVersionAsync(int version, CancellationToken cancel = default(CancellationToken)) | ||
{ | ||
if (version != 0 && !Migrations.Any(m => m.Version == version)) | ||
{ | ||
throw new ArgumentOutOfRangeException("version", $"Repository version '{version}' is unknown."); | ||
} | ||
|
||
var currentVersion = CurrentVersion; | ||
var increment = CurrentVersion < version ? 1 : -1; | ||
while (currentVersion != version) | ||
{ | ||
var nextVersion = currentVersion + increment; | ||
log.InfoFormat("Migrating to version {0}", nextVersion); | ||
|
||
if (increment > 0) | ||
{ | ||
var migration = Migrations.FirstOrDefault(m => m.Version == nextVersion); | ||
if (migration.CanUpgrade) | ||
{ | ||
await migration.UpgradeAsync(ipfs, cancel); | ||
} | ||
} | ||
else if (increment < 0) | ||
{ | ||
var migration = Migrations.FirstOrDefault(m => m.Version == currentVersion); | ||
if (migration.CanDowngrade) | ||
{ | ||
await migration.DowngradeAsync(ipfs, cancel); | ||
} | ||
} | ||
|
||
CurrentVersion = nextVersion; | ||
currentVersion = nextVersion; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets the FQN of the version file. | ||
/// </summary> | ||
/// <returns> | ||
/// The path to the version file. | ||
/// </returns> | ||
string VersionPath() | ||
{ | ||
return Path.Combine(ipfs.Options.Repository.ExistingFolder(), "version"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace Ipfs.Engine.Migration | ||
{ | ||
[TestClass] | ||
public class MigrationManagerTest | ||
{ | ||
[TestMethod] | ||
public void HasMigrations() | ||
{ | ||
var migrator = new MigrationManager(TestFixture.Ipfs); | ||
var migrations = migrator.Migrations; | ||
Assert.AreNotEqual(0, migrations.Count); | ||
} | ||
|
||
[TestMethod] | ||
public void MirgrateToUnknownVersion() | ||
{ | ||
var migrator = new MigrationManager(TestFixture.Ipfs); | ||
ExceptionAssert.Throws<ArgumentOutOfRangeException>(() => | ||
{ | ||
migrator.MirgrateToVersionAsync(int.MaxValue).Wait(); | ||
}); | ||
} | ||
|
||
[TestMethod] | ||
public async Task MigrateToLowestThenHighest() | ||
{ | ||
using (var ipfs = new TempNode()) | ||
{ | ||
var migrator = new MigrationManager(ipfs); | ||
await migrator.MirgrateToVersionAsync(0); | ||
Assert.AreEqual(0, migrator.CurrentVersion); | ||
|
||
await migrator.MirgrateToVersionAsync(migrator.LatestVersion); | ||
Assert.AreEqual(migrator.LatestVersion, migrator.CurrentVersion); | ||
} | ||
} | ||
} | ||
} | ||
|