Skip to content

Commit

Permalink
Merge pull request #244 from pjf/sanity_checks
Browse files Browse the repository at this point in the history
Enforced sanity on registry and relational checks.
  • Loading branch information
techman83 committed Nov 3, 2014
2 parents 46d7734 + 5c925b6 commit 1c9ff9e
Show file tree
Hide file tree
Showing 16 changed files with 608 additions and 70 deletions.
4 changes: 4 additions & 0 deletions CKAN/CKAN/CKAN.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,13 @@
<Compile Include="Registry\Registry.cs" />
<Compile Include="Registry\RegistryManager.cs" />
<Compile Include="Types\ModuleInstallDescriptor.cs" />
<Compile Include="Relationships\SanityChecker.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Relationships\" />
</ItemGroup>
</Project>
39 changes: 34 additions & 5 deletions CKAN/CKAN/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,9 @@ public class Module
[JsonProperty("author")] [JsonConverter(typeof (JsonSingleOrArrayConverter<string>))] public List<string> author;

[JsonProperty("comment")] public string comment;
[JsonProperty("conflicts")] public RelationshipDescriptor[] conflicts;
[JsonProperty("depends")] public RelationshipDescriptor[] depends;

[JsonProperty("conflicts")] public List<RelationshipDescriptor> conflicts;
[JsonProperty("depends")] public List<RelationshipDescriptor> depends;

[JsonProperty("download")] public Uri download;
[JsonProperty("download_size")] public long download_size;
Expand All @@ -152,17 +153,37 @@ public class Module

[JsonProperty("name")] public string name;

// TODO: Deprecate?
[JsonProperty("pre_depends")] public RelationshipDescriptor[] pre_depends;

[JsonProperty("provides")] public string[] provides;
[JsonProperty("provides")] public List<string> provides;

[JsonProperty("recommends")] public RelationshipDescriptor[] recommends;
[JsonProperty("recommends")] public List<RelationshipDescriptor> recommends;
[JsonProperty("release_status")] public string release_status; // TODO: Strong type

[JsonProperty("resources")] public ResourcesDescriptor resources;
[JsonProperty("suggests")] public RelationshipDescriptor[] suggests;
[JsonProperty("suggests")] public List<RelationshipDescriptor> suggests;
[JsonProperty("version", Required = Required.Always)] public Version version;

// A list of eveything this mod provides.
public List<string> ProvidesList
{
// TODO: Consider caching this, but not in a way that the serialiser will try and
// serialise it.
get
{
var provides = new List<string>();
provides.Add(this.identifier);

if (this.provides != null)
{
provides.AddRange(this.provides);
}

return provides;
}
}

public string serialise()
{
return JsonConvert.SerializeObject(this);
Expand Down Expand Up @@ -242,6 +263,14 @@ public bool IsCompatibleKSP(KSPVersion version)

return ksp_version.Targets(version);
}

/// <summary>
/// Returns true if this module provides the functionality requested.
/// </summary>
public bool DoesProvide(string identifier)
{
return this.identifier == identifier || this.provides.Contains(identifier);
}
}

public class CkanInvalidMetadataJson : Exception
Expand Down
114 changes: 65 additions & 49 deletions CKAN/CKAN/ModuleInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,8 @@ public NetAsyncDownloader DownloadAsync(List<CkanModule> modules)
}

/// <summary>
/// Installs all modules given a list of identifiers. Resolves dependencies.
/// The function initializes a filesystem transaction, then installs all cached mods
/// this ensures we don't waste time and bandwidth if there is an issue with any of the cached archives.
/// After this we try to download the rest of the mods (asynchronously) and install them.
/// Finally, only if everything is successful, we commit the transaction.
/// Installs all modules given a list of identifiers as a transaction. Resolves dependencies.
/// This *will* save the registry at the end of operation.
/// </summary>
//
// TODO: Break this up into smaller pieces! It's huge!
Expand Down Expand Up @@ -273,11 +270,12 @@ public void InstallList(List<string> modules, RelationshipResolverOptions option
Install(modsToInstall[i]);
}

registry_manager.Save();
transaction.Complete();
return;
}

transaction.Dispose(); // Rollback
transaction.Dispose(); // Rollback on unsuccessful download.
}
}

Expand Down Expand Up @@ -352,7 +350,8 @@ public List<string> GetModuleContentsList(CkanModule module)
/// Install our mod from the filename supplied.
/// If no file is supplied, we will check the cache or download it.
/// Does *not* resolve dependencies; this actually does the heavy listing.
/// Use InstallList() for requests from the user.
/// Does *not* save the registry.
/// Do *not* call this directly, use InstallList() instead.
///
/// </summary>
//
Expand All @@ -362,8 +361,6 @@ internal void Install(CkanModule module, string filename = null)
{
using (var transaction = new TransactionScope())
{


User.WriteLine(module.identifier + ":\n");

Version version = registry_manager.registry.InstalledVersion(module.identifier);
Expand Down Expand Up @@ -393,11 +390,10 @@ internal void Install(CkanModule module, string filename = null)
// Register our files.
registry.RegisterModule(new InstalledModule(module_files, module, DateTime.Now));

// Done! Save our registry changes!
// Finish our transaction, but *don't* save the registry; we may be in an
// intermediate, inconsistent state.
// This is fine from a transaction standpoint, as we may not have an enclosing
// transaction, and if we do, they can always roll us back.
registry_manager.Save();

transaction.Complete();
}

Expand Down Expand Up @@ -733,43 +729,58 @@ internal static void CopyZipEntry(ZipFile zipfile, ZipEntry entry, string fullPa
return;
}

public List<string> FindReverseDependencies(string modName)
/// <summary>
/// Uninstalls all the mods provided, including things which depend upon them.
/// This *DOES* save the registry.
/// Preferred over Uninstall.
/// </summary>
public void UninstallList(IEnumerable<string> mods)
{
var reverseDependencies = new List<string>();

// loop through all installed modules
foreach (var keyValue in registry_manager.registry.installed_modules)
using (var transaction = new TransactionScope())
{
Module mod = keyValue.Value.source_module;
bool isDependency = false;
// Find all the things which need uninstalling.
IEnumerable<string> goners = registry_manager.registry.FindReverseDependencies(mods);

User.WriteLine("About to remove:\n");

if (mod.depends != null)
foreach (string mod in goners)
{
foreach (RelationshipDescriptor dependency in mod.depends)
{
if (dependency.name == modName)
{
isDependency = true;
break;
}
}
User.WriteLine(" * {0}", mod);
}

bool ok = User.YesNo("\nContinue?", FrontEndType.CommandLine);

if (!ok)
{
User.WriteLine("Mod removal aborted at user request.");
return;
}

if (isDependency)
foreach (string mod in goners)
{
reverseDependencies.Add(mod.identifier);
Uninstall(mod);
}

registry_manager.Save();

transaction.Complete();
}
}

return reverseDependencies;
public void UninstallList(string mod)
{
var list = new List<string>();
list.Add(mod);
UninstallList(list);
}

/// <summary>
/// Uninstall the module provided.
/// Uninstall the module provided. For internal use only.
/// Use UninstallList for user queries, it also does dependency handling.
/// This does *NOT* save the registry.
/// </summary>

// TODO: Remove second arg; shouldn't we *always* uninstall dependencies?
public void Uninstall(string modName, bool uninstallDependencies)
private void Uninstall(string modName)
{
using (var transaction = new TransactionScope())
{
Expand All @@ -783,16 +794,6 @@ public void Uninstall(string modName, bool uninstallDependencies)
return;
}

// Find all mods that depend on this one
if (uninstallDependencies)
{
List<string> reverseDependencies = FindReverseDependencies(modName);
foreach (string reverseDependency in reverseDependencies)
{
Uninstall(reverseDependency, uninstallDependencies);
}
}

// Walk our registry to find all files for this mod.
Dictionary<string, InstalledModuleFile> files =
registry_manager.registry.installed_modules[modName].installed_files;
Expand All @@ -819,35 +820,50 @@ public void Uninstall(string modName, bool uninstallDependencies)
}
catch (Exception ex)
{
// TODO: Report why.
// XXX: This is terrible, we're catching all exceptions.
log.ErrorFormat("Failure in locating file {0} : {1}", path, ex.Message);
}
}

// Remove from registry.

registry_manager.registry.DeregisterModule(modName);
registry_manager.Save();

// TODO: We need to remove from child to parent first.
foreach (string directory in directoriesToDelete)
{
if (!Directory.GetFiles(directory).Any())
{
try
{
file_transaction.Delete(directory);
file_transaction.DeleteDirectory(directory);
}
catch (Exception)
catch (Exception ex)
{
// TODO: Report why.
User.WriteLine("Couldn't delete directory {0}", directory);
User.WriteLine("Couldn't delete directory {0} : {1}", directory, ex.Message);
}
}
else
{
User.WriteLine("Not removing directory {0}, it's not empty", directory);
}
}
transaction.Complete();
}

return;
}

/// <summary>
/// Don't use this. Use Registry.FindReverseDependencies instead.
/// This method may be deprecated in the future.
/// </summary>
// Here for now to keep the GUI happy.
public HashSet<string> FindReverseDependencies(string module)
{
return registry_manager.registry.FindReverseDependencies(module);
}

}
}
4 changes: 0 additions & 4 deletions CKAN/CKAN/NetAsyncDownloader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,10 @@ private void FileDownloadComplete(int index, Exception error)
{
log.Debug("All files finished downloading");

// verify no errors before commit

bool err = false;
for (int i = 0; i < downloads.Length; i++)
{
if (downloads[i].error != null)
{
err = true;
// TODO: XXX: Shouldn't we throw a kraken here?
log.Error("Something went wrong but I don't know what!");
}
Expand Down
Loading

0 comments on commit 1c9ff9e

Please sign in to comment.