Exemple #1
0
        async Task <string> ValidateNonExistantSqliteDBName(string databaseName, CancellationToken cancellationToken)
        {
            var resolvedPath = ioManager.ResolvePath(databaseName);

            try
            {
                var  directoryName    = ioManager.GetDirectoryName(resolvedPath);
                bool directoryExisted = await ioManager.DirectoryExists(directoryName, cancellationToken).ConfigureAwait(false);

                await ioManager.CreateDirectory(directoryName, cancellationToken).ConfigureAwait(false);

                try
                {
                    await ioManager.WriteAllBytes(resolvedPath, Array.Empty <byte>(), cancellationToken).ConfigureAwait(false);
                }
                catch
                {
                    if (!directoryExisted)
                    {
                        await ioManager.DeleteDirectory(directoryName, cancellationToken).ConfigureAwait(false);
                    }
                    throw;
                }
            }
            catch (IOException)
            {
                return(null);
            }

            if (!Path.IsPathRooted(databaseName))
            {
                await console.WriteAsync("Note, this relative path (currently) resolves to the following:", true, cancellationToken).ConfigureAwait(false);

                await console.WriteAsync(resolvedPath, true, cancellationToken).ConfigureAwait(false);

                bool writeResolved = await PromptYesNo(
                    "Would you like to save the relative path in the configuration? If not, the full path will be saved. (y/n): ",
                    cancellationToken)
                                     .ConfigureAwait(false);

                if (writeResolved)
                {
                    databaseName = resolvedPath;
                }
            }

            await ioManager.DeleteFile(databaseName, cancellationToken).ConfigureAwait(false);

            return(databaseName);
        }
        /// <inheritdoc />
        public async Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken)
        {
            async Task SymlinkBase(bool files)
            {
                Task <IReadOnlyList <string> > task;

                if (files)
                {
                    task = ioManager.GetFiles(GameStaticFilesSubdirectory, cancellationToken);
                }
                else
                {
                    task = ioManager.GetDirectories(GameStaticFilesSubdirectory, cancellationToken);
                }
                var entries = await task.ConfigureAwait(false);

                await Task.WhenAll(task.Result.Select(async x =>
                {
                    var destPath = ioManager.ConcatPath(destination, ioManager.GetFileName(x));
                    logger.LogTrace("Symlinking {0} to {1}...", x, destPath);
                    var fileExistsTask = ioManager.FileExists(destPath, cancellationToken);
                    if (await ioManager.DirectoryExists(destPath, cancellationToken).ConfigureAwait(false))
                    {
                        await ioManager.DeleteDirectory(destPath, cancellationToken).ConfigureAwait(false);
                    }
                    var fileExists = await fileExistsTask.ConfigureAwait(false);
                    if (fileExists)
                    {
                        await ioManager.DeleteFile(destPath, cancellationToken).ConfigureAwait(false);
                    }
                    await symlinkFactory.CreateSymbolicLink(ioManager.ResolvePath(x), ioManager.ResolvePath(destPath), cancellationToken).ConfigureAwait(false);
                })).ConfigureAwait(false);
            }

            using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
            {
                await EnsureDirectories(cancellationToken).ConfigureAwait(false);

                await Task.WhenAll(SymlinkBase(true), SymlinkBase(false)).ConfigureAwait(false);
            }
        }
 /// <inheritdoc />
 public async Task <IRepository> CloneRepository(Uri url, string initialBranch, string username, string password, Action <int> progressReporter, CancellationToken cancellationToken)
 {
     lock (this)
     {
         if (CloneInProgress)
         {
             throw new InvalidOperationException("The repository is already being cloned!");
         }
         CloneInProgress = true;
     }
     try
     {
         using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
             if (!await ioManager.DirectoryExists(".", cancellationToken).ConfigureAwait(false))
             {
                 try
                 {
                     await Task.Factory.StartNew(() =>
                     {
                         string path = null;
                         try
                         {
                             path = LibGit2Sharp.Repository.Clone(url.ToString(), ioManager.ResolvePath("."), new CloneOptions
                             {
                                 OnProgress         = (a) => !cancellationToken.IsCancellationRequested,
                                 OnTransferProgress = (a) =>
                                 {
                                     var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
                                     progressReporter((int)percentage);
                                     return(!cancellationToken.IsCancellationRequested);
                                 },
                                 RecurseSubmodules           = true,
                                 OnUpdateTips                = (a, b, c) => !cancellationToken.IsCancellationRequested,
                                 RepositoryOperationStarting = (a) => !cancellationToken.IsCancellationRequested,
                                 BranchName          = initialBranch,
                                 CredentialsProvider = (a, b, c) => username != null ? (Credentials) new UsernamePasswordCredentials
                                 {
                                     Username = username,
                                     Password = password
                                 } : new DefaultCredentials()
                             });
                         }
                         catch (UserCancelledException) { }
                         cancellationToken.ThrowIfCancellationRequested();
                     }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
                 }
                 catch
                 {
                     try
                     {
                         await ioManager.DeleteDirectory(".", default).ConfigureAwait(false);
                     }
                     catch { }
                     throw;
                 }
             }
             else
             {
                 return(null);
             }
     }
     finally
     {
         CloneInProgress = false;
     }
     return(await LoadRepository(cancellationToken).ConfigureAwait(false));
 }
Exemple #4
0
        public async Task <IActionResult> Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            if (String.IsNullOrWhiteSpace(model.Name))
            {
                return(BadRequest(new ErrorMessage(ErrorCode.InstanceWhitespaceName)));
            }

            var unNormalizedPath   = model.Path;
            var targetInstancePath = NormalizePath(unNormalizedPath);

            model.Path = targetInstancePath;

            var installationDirectoryPath = NormalizePath(DefaultIOManager.CurrentDirectory);

            bool InstanceIsChildOf(string otherPath)
            {
                if (!targetInstancePath.StartsWith(otherPath, StringComparison.Ordinal))
                {
                    return(false);
                }

                bool sameLength       = targetInstancePath.Length == otherPath.Length;
                char dirSeparatorChar = targetInstancePath.ToCharArray()[Math.Min(otherPath.Length, targetInstancePath.Length - 1)];

                return(sameLength ||
                       dirSeparatorChar == Path.DirectorySeparatorChar ||
                       dirSeparatorChar == Path.AltDirectorySeparatorChar);
            }

            if (InstanceIsChildOf(installationDirectoryPath))
            {
                return(Conflict(new ErrorMessage(ErrorCode.InstanceAtConflictingPath)));
            }

            // Validate it's not a child of any other instance
            IActionResult earlyOut = null;
            ulong         countOfOtherInstances = 0;

            using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
            {
                var newCancellationToken = cts.Token;
                try
                {
                    await DatabaseContext
                    .Instances
                    .AsQueryable()
                    .Select(x => new Models.Instance
                    {
                        Path = x.Path
                    })
                    .ForEachAsync(
                        otherInstance =>
                    {
                        if (++countOfOtherInstances >= generalConfiguration.InstanceLimit)
                        {
                            earlyOut ??= Conflict(new ErrorMessage(ErrorCode.InstanceLimitReached));
                        }
                        else if (InstanceIsChildOf(otherInstance.Path))
                        {
                            earlyOut ??= Conflict(new ErrorMessage(ErrorCode.InstanceAtConflictingPath));
                        }

                        if (earlyOut != null && !newCancellationToken.IsCancellationRequested)
                        {
                            cts.Cancel();
                        }
                    },
                        newCancellationToken)
                    .ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }
            }

            if (earlyOut != null)
            {
                return(earlyOut);
            }

            // Last test, ensure it's in the list of valid paths
            if (!(generalConfiguration.ValidInstancePaths?
                  .Select(path => NormalizePath(path))
                  .Any(path => InstanceIsChildOf(path)) ?? true))
            {
                return(BadRequest(new ErrorMessage(ErrorCode.InstanceNotAtWhitelistedPath)));
            }

            async Task <bool> DirExistsAndIsNotEmpty()
            {
                if (!await ioManager.DirectoryExists(model.Path, cancellationToken).ConfigureAwait(false))
                {
                    return(false);
                }

                var filesTask = ioManager.GetFiles(model.Path, cancellationToken);
                var dirsTask  = ioManager.GetDirectories(model.Path, cancellationToken);

                var files = await filesTask.ConfigureAwait(false);

                var dirs = await dirsTask.ConfigureAwait(false);

                return(files.Concat(dirs).Any());
            }

            var  dirExistsTask = DirExistsAndIsNotEmpty();
            bool attached      = false;

            if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
            {
                if (!await ioManager.FileExists(ioManager.ConcatPath(model.Path, InstanceAttachFileName), cancellationToken).ConfigureAwait(false))
                {
                    return(Conflict(new ErrorMessage(ErrorCode.InstanceAtExistingPath)));
                }
                else
                {
                    attached = true;
                }
            }

            var newInstance = CreateDefaultInstance(model);

            DatabaseContext.Instances.Add(newInstance);
            try
            {
                await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);

                try
                {
                    // actually reserve it now
                    await ioManager.CreateDirectory(unNormalizedPath, cancellationToken).ConfigureAwait(false);

                    await ioManager.DeleteFile(ioManager.ConcatPath(targetInstancePath, InstanceAttachFileName), cancellationToken).ConfigureAwait(false);
                }
                catch
                {
                    // oh shit delete the model
                    DatabaseContext.Instances.Remove(newInstance);

                    // DCT: Operation must always run
                    await DatabaseContext.Save(default).ConfigureAwait(false);
        /// <inheritdoc />
        public async Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken)
        {
            async Task <IReadOnlyList <string> > GetIgnoreFiles()
            {
                var ignoreFileBytes = await ioManager.ReadAllBytes(StaticIgnorePath(), cancellationToken).ConfigureAwait(false);

                var ignoreFileText = Encoding.UTF8.GetString(ignoreFileBytes);

                var results = new List <string> {
                    StaticIgnoreFile
                };

                //we don't want to lose trailing whitespace on linux
                using (var reader = new StringReader(ignoreFileText))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var line = await reader.ReadLineAsync().ConfigureAwait(false);

                    if (!String.IsNullOrEmpty(line))
                    {
                        results.Add(line);
                    }
                }

                return(results);
            };

            IReadOnlyList <string> ignoreFiles;

            async Task SymlinkBase(bool files)
            {
                Task <IReadOnlyList <string> > task;

                if (files)
                {
                    task = ioManager.GetFiles(GameStaticFilesSubdirectory, cancellationToken);
                }
                else
                {
                    task = ioManager.GetDirectories(GameStaticFilesSubdirectory, cancellationToken);
                }
                var entries = await task.ConfigureAwait(false);

                await Task.WhenAll(entries.Select(async x =>
                {
                    var fileName = ioManager.GetFileName(x);

                    bool ignored;
                    if (platformIdentifier.IsWindows)
                    {
                        //need to normalize
                        ignored = ignoreFiles.Any(y => fileName.ToUpperInvariant() == y.ToUpperInvariant());
                    }
                    else
                    {
                        ignored = ignoreFiles.Any(y => fileName == y);
                    }

                    if (ignored)
                    {
                        logger.LogTrace("Ignoring static file {0}...", fileName);
                        return;
                    }

                    var destPath = ioManager.ConcatPath(destination, fileName);
                    logger.LogTrace("Symlinking {0} to {1}...", x, destPath);
                    var fileExistsTask = ioManager.FileExists(destPath, cancellationToken);
                    if (await ioManager.DirectoryExists(destPath, cancellationToken).ConfigureAwait(false))
                    {
                        await ioManager.DeleteDirectory(destPath, cancellationToken).ConfigureAwait(false);
                    }
                    var fileExists = await fileExistsTask.ConfigureAwait(false);
                    if (fileExists)
                    {
                        await ioManager.DeleteFile(destPath, cancellationToken).ConfigureAwait(false);
                    }
                    await symlinkFactory.CreateSymbolicLink(ioManager.ResolvePath(x), ioManager.ResolvePath(destPath), cancellationToken).ConfigureAwait(false);
                })).ConfigureAwait(false);
            }

            using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
            {
                await EnsureDirectories(cancellationToken).ConfigureAwait(false);

                ignoreFiles = await GetIgnoreFiles().ConfigureAwait(false);

                await Task.WhenAll(SymlinkBase(true), SymlinkBase(false)).ConfigureAwait(false);
            }
        }
        public async Task <IActionResult> Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            if (String.IsNullOrWhiteSpace(model.Name))
            {
                return(BadRequest(new ErrorMessage {
                    Message = "name must not be empty!"
                }));
            }

            if (model.Path == null)
            {
                return(BadRequest(new ErrorMessage {
                    Message = "path must not be empty!"
                }));
            }

            NormalizeModelPath(model, out var rawPath);

            var localPath = ioManager.ResolvePath(".");

            NormalizeModelPath(new Api.Models.Instance
            {
                Path = localPath
            }, out var normalizedLocalPath);

            if (rawPath.StartsWith(normalizedLocalPath, StringComparison.Ordinal))
            {
                bool sameLength       = rawPath.Length == normalizedLocalPath.Length;
                char dirSeparatorChar = rawPath.ToCharArray()[normalizedLocalPath.Length];
                if (sameLength ||
                    dirSeparatorChar == Path.DirectorySeparatorChar ||
                    dirSeparatorChar == Path.AltDirectorySeparatorChar)
                {
                    return(Conflict(new ErrorMessage {
                        Message = "Instances cannot be created in the installation directory!"
                    }));
                }
            }

            var  dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken);
            bool attached      = false;

            if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
            {
                if (!await ioManager.FileExists(ioManager.ConcatPath(model.Path, InstanceAttachFileName), cancellationToken).ConfigureAwait(false))
                {
                    return(Conflict(new ErrorMessage {
                        Message = "Path not empty!"
                    }));
                }
                else
                {
                    attached = true;
                }
            }

            var newInstance = new Models.Instance
            {
                ConfigurationType   = model.ConfigurationType ?? ConfigurationType.Disallowed,
                DreamDaemonSettings = new DreamDaemonSettings
                {
                    AllowWebClient = false,
                    AutoStart      = false,
                    PrimaryPort    = 1337,
                    SecondaryPort  = 1338,
                    SecurityLevel  = DreamDaemonSecurity.Safe,
                    SoftRestart    = false,
                    SoftShutdown   = false,
                    StartupTimeout = 20
                },
                DreamMakerSettings = new DreamMakerSettings
                {
                    ApiValidationPort          = 1339,
                    ApiValidationSecurityLevel = DreamDaemonSecurity.Safe
                },
                Name               = model.Name,
                Online             = false,
                Path               = model.Path,
                AutoUpdateInterval = model.AutoUpdateInterval ?? 0,
                RepositorySettings = new RepositorySettings
                {
                    CommitterEmail            = "*****@*****.**",
                    CommitterName             = application.VersionPrefix,
                    PushTestMergeCommits      = false,
                    ShowTestMergeCommitters   = false,
                    AutoUpdatesKeepTestMerges = false,
                    AutoUpdatesSynchronize    = false,
                    PostTestMergeComment      = false
                },
                InstanceUsers = new List <Models.InstanceUser>                // give this user full privileges on the instance
                {
                    InstanceAdminUser()
                }
            };

            DatabaseContext.Instances.Add(newInstance);
            try
            {
                await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);

                try
                {
                    // actually reserve it now
                    await ioManager.CreateDirectory(rawPath, cancellationToken).ConfigureAwait(false);

                    await ioManager.DeleteFile(ioManager.ConcatPath(rawPath, InstanceAttachFileName), cancellationToken).ConfigureAwait(false);
                }
                catch
                {
                    // oh shit delete the model
                    DatabaseContext.Instances.Remove(newInstance);

                    await DatabaseContext.Save(default).ConfigureAwait(false);
Exemple #7
0
        /// <inheritdoc />
        public async Task <IRepository> CloneRepository(Uri url, string initialBranch, string username, string password, Action <int> progressReporter, CancellationToken cancellationToken)
        {
            if (url == null)
            {
                throw new ArgumentNullException(nameof(url));
            }
            if (progressReporter == null)
            {
                throw new ArgumentNullException(nameof(progressReporter));
            }

            logger.LogInformation("Begin clone {0} (Branch: {1})", url, initialBranch);
            lock (semaphore)
            {
                if (CloneInProgress)
                {
                    throw new JobException(ErrorCode.RepoCloning);
                }
                CloneInProgress = true;
            }

            try
            {
                using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
                {
                    logger.LogTrace("Semaphore acquired");
                    var repositoryPath = ioManager.ResolvePath();
                    if (!await ioManager.DirectoryExists(repositoryPath, cancellationToken).ConfigureAwait(false))
                    {
                        try
                        {
                            var cloneOptions = new CloneOptions
                            {
                                OnProgress         = (a) => !cancellationToken.IsCancellationRequested,
                                OnTransferProgress = (a) =>
                                {
                                    var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
                                    progressReporter((int)percentage);
                                    return(!cancellationToken.IsCancellationRequested);
                                },
                                RecurseSubmodules           = true,
                                OnUpdateTips                = (a, b, c) => !cancellationToken.IsCancellationRequested,
                                RepositoryOperationStarting = (a) => !cancellationToken.IsCancellationRequested,
                                BranchName          = initialBranch,
                                CredentialsProvider = repositoryFactory.GenerateCredentialsHandler(username, password)
                            };

                            await repositoryFactory.Clone(
                                url,
                                cloneOptions,
                                repositoryPath,
                                cancellationToken)
                            .ConfigureAwait(false);
                        }
                        catch
                        {
                            try
                            {
                                logger.LogTrace("Deleting partially cloned repository...");
                                await ioManager.DeleteDirectory(repositoryPath, default).ConfigureAwait(false);
                            }
                            catch (Exception e)
                            {
                                logger.LogDebug("Error deleting partially cloned repository! Exception: {0}", e);
                            }

                            throw;
                        }
                    }
                    else
                    {
                        logger.LogDebug("Repository exists, clone aborted!");
                        return(null);
                    }
                }

                logger.LogInformation("Clone complete!");
            }
            finally
            {
                CloneInProgress = false;
            }

            return(await LoadRepository(cancellationToken).ConfigureAwait(false));
        }
Exemple #8
0
        /// <inheritdoc />
        public async Task <IRepository> CloneRepository(Uri url, string initialBranch, string username, string password, Action <int> progressReporter, CancellationToken cancellationToken)
        {
            if (url == null)
            {
                throw new ArgumentNullException(nameof(url));
            }
            if (progressReporter == null)
            {
                throw new ArgumentNullException(nameof(progressReporter));
            }

            logger.LogInformation("Begin clone {0} (Branch: {1})", url, initialBranch);
            lock (this)
            {
                if (CloneInProgress)
                {
                    throw new InvalidOperationException("The repository is already being cloned!");
                }
                CloneInProgress = true;
            }

            try
            {
                using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
                {
                    logger.LogTrace("Semaphore acquired");
                    if (!await ioManager.DirectoryExists(".", cancellationToken).ConfigureAwait(false))
                    {
                        try
                        {
                            await Task.Factory.StartNew(() =>
                            {
                                string path = null;
                                try
                                {
                                    path = LibGit2Sharp.Repository.Clone(url.ToString(), ioManager.ResolvePath("."), new CloneOptions
                                    {
                                        OnProgress         = (a) => !cancellationToken.IsCancellationRequested,
                                        OnTransferProgress = (a) =>
                                        {
                                            var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
                                            progressReporter((int)percentage);
                                            return(!cancellationToken.IsCancellationRequested);
                                        },
                                        RecurseSubmodules           = true,
                                        OnUpdateTips                = (a, b, c) => !cancellationToken.IsCancellationRequested,
                                        RepositoryOperationStarting = (a) => !cancellationToken.IsCancellationRequested,
                                        BranchName          = initialBranch,
                                        CredentialsProvider = credentialsProvider.GenerateHandler(username, password)
                                    });
                                }
                                catch (UserCancelledException) { }
                                cancellationToken.ThrowIfCancellationRequested();
                            }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
                        }
                        catch
                        {
                            try
                            {
                                logger.LogTrace("Deleting partially cloned repository...");
                                await ioManager.DeleteDirectory(".", default).ConfigureAwait(false);
                            }
                            catch (Exception e)
                            {
                                logger.LogDebug("Error deleting partially cloned repository! Exception: {0}", e);
                            }

                            throw;
                        }
                    }
                    else
                    {
                        logger.LogDebug("Repository exists, clone aborted!");
                        return(null);
                    }
                }

                logger.LogInformation("Clone complete!");
            }
            finally
            {
                CloneInProgress = false;
            }

            return(await LoadRepository(cancellationToken).ConfigureAwait(false));
        }
Exemple #9
0
        /// <inheritdoc />
        public Task Initialize(CancellationToken cancellationToken)
        {
            return(Task.Factory.StartNew(async() =>
            {
                using (logger.BeginScope("Initializing repository..."))
                {
                    var repoPath = ioManager.ResolvePath(ioManager.ConcatPath(gitHubConfiguration.RepoOwner, gitHubConfiguration.RepoName));

                    logger.LogTrace("Repo path evaluated to be: {0}", repoPath);

                    try
                    {
                        logger.LogTrace("Creating repository object.");
                        cancellationToken.ThrowIfCancellationRequested();
                        repositoryObject = new LibGit2Sharp.Repository(repoPath);

                        repositoryObject.RemoveUntrackedFiles();

                        cancellationToken.ThrowIfCancellationRequested();

                        repositoryObject.RetrieveStatus();
                    }
                    catch (OperationCanceledException e)
                    {
                        logger.LogDebug(e, "Repository setup cancelled!");
                        repositoryObject?.Dispose();
                        throw;
                    }
                    catch (Exception e)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                        using (logger.BeginScope("Repository fallback initializing..."))
                        {
                            repositoryObject?.Dispose();
                            try
                            {
                                logger.LogTrace("Checking repository directory exists.");
                                if (await ioManager.DirectoryExists(repoPath, cancellationToken).ConfigureAwait(false))
                                {
                                    logger.LogWarning(e, "Failed to load repository! Deleting and cloning...");
                                    await ioManager.DeleteDirectory(repoPath, cancellationToken).ConfigureAwait(false);
                                }
                                else
                                {
                                    logger.LogInformation(e, "Cloning repository...");
                                }

                                LibGit2Sharp.Repository.Clone(String.Format(CultureInfo.InvariantCulture, "https://github.com/{0}/{1}", gitHubConfiguration.RepoOwner, gitHubConfiguration.RepoName), repoPath, new CloneOptions
                                {
                                    Checkout = false,
                                    RecurseSubmodules = true,
                                    OnProgress = (a) => !cancellationToken.IsCancellationRequested,
                                    OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested,
                                    OnTransferProgress = (a) => !cancellationToken.IsCancellationRequested
                                });

                                logger.LogInformation("Repo clone completed.");

                                repositoryObject = new LibGit2Sharp.Repository(repoPath);
                            }
                            catch (UserCancelledException e2)
                            {
                                logger.LogDebug(e2, "Repository setup cancelled!");
                                cancellationToken.ThrowIfCancellationRequested();
                            }
                            catch (Exception e2)
                            {
                                logger.LogCritical(e2, "Unable to clone repository!");
                                throw;
                            }
                        }
                    }
                }
            }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current));
        }
Exemple #10
0
        public async Task <IActionResult> Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            if (String.IsNullOrWhiteSpace(model.Name))
            {
                return(BadRequest(new ErrorMessage(ErrorCode.InstanceWhitespaceName)));
            }

            var targetInstancePath = NormalizePath(model.Path);

            model.Path = targetInstancePath;

            var installationDirectoryPath = NormalizePath(DefaultIOManager.CurrentDirectory);

            bool InstanceIsChildOf(string otherPath)
            {
                if (!targetInstancePath.StartsWith(otherPath, StringComparison.Ordinal))
                {
                    return(false);
                }

                bool sameLength       = targetInstancePath.Length == otherPath.Length;
                char dirSeparatorChar = targetInstancePath.ToCharArray()[Math.Min(otherPath.Length, targetInstancePath.Length - 1)];

                return(sameLength ||
                       dirSeparatorChar == Path.DirectorySeparatorChar ||
                       dirSeparatorChar == Path.AltDirectorySeparatorChar);
            }

            if (InstanceIsChildOf(installationDirectoryPath))
            {
                return(Conflict(new ErrorMessage(ErrorCode.InstanceAtConflictingPath)));
            }

            // Validate it's not a child of any other instance
            IActionResult earlyOut = null;
            ulong         countOfOtherInstances = 0;

            using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
            {
                var newCancellationToken = cts.Token;
                try
                {
                    await DatabaseContext
                    .Instances
                    .AsQueryable()
                    .Select(x => new Models.Instance
                    {
                        Path = x.Path
                    })
                    .ForEachAsync(
                        otherInstance =>
                    {
                        if (++countOfOtherInstances >= generalConfiguration.InstanceLimit)
                        {
                            earlyOut ??= Conflict(new ErrorMessage(ErrorCode.InstanceLimitReached));
                        }
                        else if (InstanceIsChildOf(otherInstance.Path))
                        {
                            earlyOut ??= Conflict(new ErrorMessage(ErrorCode.InstanceAtConflictingPath));
                        }

                        if (earlyOut != null && !newCancellationToken.IsCancellationRequested)
                        {
                            cts.Cancel();
                        }
                    },
                        newCancellationToken)
                    .ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }
            }

            if (earlyOut != null)
            {
                return(earlyOut);
            }

            // Last test, ensure it's in the list of valid paths
            if (!(generalConfiguration.ValidInstancePaths?
                  .Select(path => NormalizePath(path))
                  .Any(path => InstanceIsChildOf(path)) ?? true))
            {
                return(BadRequest(new ErrorMessage(ErrorCode.InstanceNotAtWhitelistedPath)));
            }

            async Task <bool> DirExistsAndIsNotEmpty()
            {
                if (!await ioManager.DirectoryExists(model.Path, cancellationToken).ConfigureAwait(false))
                {
                    return(false);
                }

                var filesTask = ioManager.GetFiles(model.Path, cancellationToken);
                var dirsTask  = ioManager.GetDirectories(model.Path, cancellationToken);

                var files = await filesTask.ConfigureAwait(false);

                var dirs = await dirsTask.ConfigureAwait(false);

                return(files.Concat(dirs).Any());
            }

            var  dirExistsTask = DirExistsAndIsNotEmpty();
            bool attached      = false;

            if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
            {
                if (!await ioManager.FileExists(ioManager.ConcatPath(model.Path, InstanceAttachFileName), cancellationToken).ConfigureAwait(false))
                {
                    return(Conflict(new ErrorMessage(ErrorCode.InstanceAtExistingPath)));
                }
                else
                {
                    attached = true;
                }
            }

            var newInstance = new Models.Instance
            {
                ConfigurationType   = model.ConfigurationType ?? ConfigurationType.Disallowed,
                DreamDaemonSettings = new DreamDaemonSettings
                {
                    AllowWebClient   = false,
                    AutoStart        = false,
                    PrimaryPort      = 1337,
                    SecondaryPort    = 1338,
                    SecurityLevel    = DreamDaemonSecurity.Safe,
                    StartupTimeout   = 60,
                    HeartbeatSeconds = 60
                },
                DreamMakerSettings = new DreamMakerSettings
                {
                    ApiValidationPort          = 1339,
                    ApiValidationSecurityLevel = DreamDaemonSecurity.Safe
                },
                Name               = model.Name,
                Online             = false,
                Path               = model.Path,
                AutoUpdateInterval = model.AutoUpdateInterval ?? 0,
                ChatBotLimit       = model.ChatBotLimit ?? Models.Instance.DefaultChatBotLimit,
                RepositorySettings = new RepositorySettings
                {
                    CommitterEmail            = "*****@*****.**",
                    CommitterName             = assemblyInformationProvider.VersionPrefix,
                    PushTestMergeCommits      = false,
                    ShowTestMergeCommitters   = false,
                    AutoUpdatesKeepTestMerges = false,
                    AutoUpdatesSynchronize    = false,
                    PostTestMergeComment      = false
                },
                InstanceUsers = new List <Models.InstanceUser>                // give this user full privileges on the instance
                {
                    InstanceAdminUser()
                }
            };

            DatabaseContext.Instances.Add(newInstance);
            try
            {
                await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);

                try
                {
                    // actually reserve it now
                    await ioManager.CreateDirectory(targetInstancePath, cancellationToken).ConfigureAwait(false);

                    await ioManager.DeleteFile(ioManager.ConcatPath(targetInstancePath, InstanceAttachFileName), cancellationToken).ConfigureAwait(false);
                }
                catch
                {
                    // oh shit delete the model
                    DatabaseContext.Instances.Remove(newInstance);

                    await DatabaseContext.Save(default).ConfigureAwait(false);