protected override void ConfigImpl()
        {
            CommandPhase1 <CreateAppCommand>("CreateApp-Phase 1",
                                             obs => obs.Do(m => m.Reporter.Send(DeploymentMessages.RegisterRepository))
                                             .Select(m => Msg.New(new RegisterRepository(m.Event.TargetRepo, true), m)),
                                             (command, reporter, op) => new ContinueCreateApp(op, command, reporter));

            CommandPhase2 <ContinueCreateApp, CreateAppCommand, AppInfo>("CreateApp-Phase 2",
                                                                         obs => obs.ConditionalSelect()
                                                                         .ToResult <AppInfo?>(b =>
            {
                b.When(m => !m.Result.Ok, o => o.ApplyWhen(m => !m.Reporter.IsCompled,
                                                           data => data.Reporter.Compled(OperationResult.Failure(data.Result.Error ??
                                                                                                                 BuildErrorCodes.CommandErrorRegisterRepository)))
                       .Select(_ => default(AppInfo)));

                b.When(m => m.AppData != null, o => o.Do(m => m.Reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandDuplicateApp)))
                       .Select(_ => default(AppInfo)));

                b.When(m => m.AppData == null,
                       o => o.Select(m =>
                {
                    var data = new AppData(
                        ImmutableList <AppFileInfo> .Empty, m.Command.AppName, -1, DateTime.UtcNow,
                        DateTime.MinValue, m.Command.TargetRepo, m.Command.ProjectName);
                    m.State.Apps.Add(data);
                    return(Data: data.ToInfo(), Tracker: m.State.ChangeTracker);
                })
                       .Do(i => i.Tracker.Tell(i.Data))
                       .Select(i => i.Data));
            }));

            DirectCommandPhase1 <PushVersionCommand>("PushVersion-Phase 1",
                                                     obs => obs.Select(m => (App: m.State.Apps.Get(m.Event.AppName), Data: m))
                                                     .ConditionalSelect()
                                                     .ToResult <Unit>(
                                                         b =>
            {
                b.When(m => m.App == null, o => o.ToUnit(d => d.Data.Reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandAppNotFound))));

                b.When(m => m.App != null,
                       o => o.ToUnit(d => BuildRequest.SendWork(d.Data.State.WorkDistributor, d.Data.Reporter, d.App, d.Data.State.Repository, BuildEnv.TempFiles.CreateFile())
                                     .PipeTo(Self,
                                             success: c => new ContinuePushNewVersion(OperationResult.Success(c), d.Data.Event, d.Data.Reporter),
                                             failure: e => new ContinuePushNewVersion(OperationResult.Failure(e), d.Data.Event, d.Data.Reporter))));
            }));

            CommandPhase2 <ContinuePushNewVersion, PushVersionCommand, AppBinary>("PushVersion-Phase 2",
                                                                                  obs => obs.ConditionalSelect()
                                                                                  .ToResult <AppBinary?>(
                                                                                      b =>
            {
                b.When(m => m.AppData == null, o => o.Do(m => m.Reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandAppNotFound)))
                       .Select(_ => default(AppBinary)));

                b.When(m => m.AppData != null, o => o.SelectMany(UpdateAppData));
Beispiel #2
0
        public AppCommandProcessor(IMongoCollection <AppData> apps, GridFSBucket files, RepositoryApi repository, DataTransferManager dataTransfer,
                                   IMongoCollection <ToDeleteRevision> toDelete, WorkDistributor <BuildRequest, BuildCompled> workDistributor, IActorRef changeTracker)
        {
            _apps = apps;

            CommandPhase1 <CreateAppCommand>("CreateApp", repository,
                                             (command, reporter) =>
            {
                reporter.Send(DeploymentMessages.RegisterRepository);
                return(new RegisterRepository(command.TargetRepo)
                {
                    IgnoreDuplicate = true
                });
            },
                                             (command, reporter, op) => new ContinueCreateApp(op, command, reporter));

            CommandPhase2 <ContinueCreateApp, CreateAppCommand, AppInfo>("CreateApp2", (command, result, reporter, data) =>
            {
                if (!result.Ok)
                {
                    if (reporter.IsCompled)
                    {
                        return(null);
                    }
                    reporter.Compled(OperationResult.Failure(result.Error ?? BuildErrorCodes.CommandErrorRegisterRepository));
                    return(null);
                }

                if (data != null)
                {
                    reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandDuplicateApp));
                    return(null);
                }

                var newData = new AppData(command.AppName, -1, DateTime.UtcNow, DateTime.MinValue, command.TargetRepo, command.ProjectName, ImmutableList <AppFileInfo> .Empty);

                apps.InsertOne(newData);
                var info = newData.ToInfo();

                changeTracker.Tell(info);
                return(info);
            });

            CommandPhase1 <PushVersionCommand>("PushVersion",
                                               (command, reporter) =>
            {
                var data = apps.AsQueryable().FirstOrDefault(ad => ad.Name == command.AppName);
                if (data == null)
                {
                    reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandAppNotFound));
                }
                else
                {
                    BuildRequest.SendWork(workDistributor, reporter, data, repository, BuildEnv.TempFiles.CreateFile())
                    .PipeTo(Self,
                            success: c => new ContinuePushNewVersion(OperationResult.Success(c), command, reporter),
                            failure: e => new ContinuePushNewVersion(OperationResult.Failure(e.Unwrap()?.Message ?? "Cancel"), command, reporter));
                }
            });

            CommandPhase2 <ContinuePushNewVersion, PushVersionCommand, AppBinary>("PushVersion2", (command, result, reporter, data) =>
            {
                if (data == null)
                {
                    if (!reporter.IsCompled)
                    {
                        reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandAppNotFound));
                    }
                    return(null);
                }

                if (!result.Ok)
                {
                    return(null);
                }

                using var transaction = apps.Database.Client.StartSession(new ClientSessionOptions { DefaultTransactionOptions = new TransactionOptions(writeConcern: WriteConcern.Acknowledged) });
                var dataFilter        = Builders <AppData> .Filter.Eq(ad => ad.Name, data.Name);

                var(commit, fileName) = ((string, ITempFile))result.Outcome !;

                using var targetStream = fileName;

                var newId = files.UploadFromStream(data.Name + ".zip", targetStream.Stream);

                var newBinary  = new AppFileInfo(newId, data.Last + 1, DateTime.UtcNow, false, commit);
                var newBinarys = data.Versions.Add(newBinary);

                var definition = Builders <AppData> .Update;
                var updates    = new List <UpdateDefinition <AppData> >
                {
                    definition.Set(ad => ad.Last, newBinary.Version),
                    definition.Set(ad => ad.Versions, newBinarys)
                };

                var deleteUpdates = new List <ToDeleteRevision>();

                if (data.Versions.Count(s => !s.Deleted) > 5)
                {
                    foreach (var info in newBinarys.OrderByDescending(i => i.CreationTime).Skip(5))
                    {
                        if (info.Deleted)
                        {
                            continue;
                        }
                        info.Deleted = true;
                        deleteUpdates.Add(new ToDeleteRevision(info.File.ToString()));
                    }
                }

                transaction.StartTransaction();

                if (deleteUpdates.Count != 0)
                {
                    toDelete.InsertMany(transaction, deleteUpdates);
                }
                if (!apps.UpdateOne(transaction, dataFilter, definition.Combine(updates)).IsAcknowledged)
                {
                    transaction.AbortTransaction();
                    reporter.Compled(OperationResult.Failure(BuildErrorCodes.DatabaseError));
                    return(null);
                }

                transaction.CommitTransaction();

                changeTracker.Tell(_apps.AsQueryable().FirstOrDefault(ad => ad.Name == command.AppName));
                return(new AppBinary(command.AppName, newBinary.Version, newBinary.CreationTime, false, newBinary.Commit, data.Repository));
            });