public void CleanDacPacScript_Test()
        {
            string raw = Properties.Resources.SyncScriptRaw;
            string cleaned;
            var    stat = DacPacHelper.CleanDacPacScript(raw, out cleaned);

            Assert.AreEqual(DacpacDeltasStatus.Success, stat);
            Assert.IsTrue(raw.Length > cleaned.Length);
            Assert.AreNotEqual(raw, cleaned);
        }
        public void ScriptDacPacDelta_InSync_Test()
        {
            string workingDir   = @"C:\temp";
            string platinumPath = Path.Combine(workingDir, "PlatinumSchema_simple.dacpac");

            File.WriteAllBytes(platinumPath, Properties.Resources.PlatinumSchema_simple);

            string result = DacPacHelper.ScriptDacPacDeltas(platinumPath, platinumPath, workingDir, false, false);

            Assert.IsNotNull(result);
            Assert.IsFalse(string.IsNullOrEmpty(result));
        }
        public void CreateSbmFromDacPacDifferences_InSync_Test()
        {
            string workingDir   = @"C:\temp";
            string platinumPath = Path.Combine(workingDir, "PlatinumSchema_simple.dacpac");

            File.WriteAllBytes(platinumPath, Properties.Resources.PlatinumSchema_simple);
            string buildFileName = Path.GetTempFileName();

            var result = DacPacHelper.CreateSbmFromDacPacDifferences(platinumPath, platinumPath, false, string.Empty, 500, false, out buildFileName);

            Assert.IsTrue(result == DacpacDeltasStatus.InSync);
            Assert.IsTrue(string.IsNullOrEmpty(buildFileName));
        }
        public void CreateSbmFromDacPacDifferences1_Success_Test()
        {
            string workingDir    = @"C:\temp";
            string platinumPath  = Path.Combine(workingDir, "PlatinumSchema1.dacpac");
            string tarnishedPath = Path.Combine(workingDir, "TarnishedSchema1.dacpac");

            File.WriteAllBytes(platinumPath, Properties.Resources.Platinumschema1);
            File.WriteAllBytes(tarnishedPath, Properties.Resources.TarnishedSchema1);
            string buildFileName = Path.GetTempFileName();

            var result = DacPacHelper.CreateSbmFromDacPacDifferences(platinumPath, tarnishedPath, false, string.Empty, 500, false, out buildFileName);

            Assert.IsTrue(result == DacpacDeltasStatus.Success);
            Assert.IsTrue(File.ReadAllBytes(buildFileName).Length > 0);
        }
        /// <summary>
        /// Using the CommandLineArgs values, determines where the scripts will come from (raw scripts, SBM, DACPAC or generated DACPAC)
        /// </summary>
        /// <param name="cmdLine"></param>
        /// <returns></returns>
        private (int, CommandLineArgs) ConfigureScriptSource(CommandLineArgs cmdLine)
        {
            //If we don't have a pre-constructed build file, but rather a script source directory, we'll build one from there...
            if (!string.IsNullOrWhiteSpace(cmdLine.ScriptSrcDir))
            {
                ConstructBuildFileFromScriptDirectory(cmdLine.ScriptSrcDir);
            }
            else if (!string.IsNullOrWhiteSpace(cmdLine.BuildFileName)) //using SBM as a source
            {
                ThreadedExecution.buildZipFileName = cmdLine.BuildFileName;
                string msg = "--packagename setting found. Using '" + ThreadedExecution.buildZipFileName + "' as build source";
                WriteToLog(new LogMsg()
                {
                    Message = msg, LogType = LogType.Message
                });
                log.LogInformation(msg);
            }
            else if (!string.IsNullOrWhiteSpace(cmdLine.DacPacArgs.PlatinumDacpac)) //using a platinum dacpac as a source
            {
                ThreadedExecution.platinumDacPacFileName = cmdLine.DacPacArgs.PlatinumDacpac;
                string msg = "--platinumdacpac setting found. Using '" + ThreadedExecution.platinumDacPacFileName + "' as build source";
                WriteToLog(new LogMsg()
                {
                    Message = msg, LogType = LogType.Message
                });;
                log.LogInformation(msg);
            }
            else if (!string.IsNullOrWhiteSpace(cmdLine.DacPacArgs.PlatinumDbSource) && !string.IsNullOrWhiteSpace(cmdLine.DacPacArgs.PlatinumServerSource)) //using a platinum database as the source
            {
                log.LogInformation($"Extracting Platinum Dacpac from {cmdLine.DacPacArgs.PlatinumServerSource} : {cmdLine.DacPacArgs.PlatinumDbSource}");
                string dacpacName = Path.Combine(ThreadedExecution.rootLoggingPath, cmdLine.DacPacArgs.PlatinumDbSource + ".dacpac");

                if (!DacPacHelper.ExtractDacPac(cmdLine.DacPacArgs.PlatinumDbSource, cmdLine.DacPacArgs.PlatinumServerSource, cmdLine.AuthenticationArgs.AuthenticationType, cmdLine.AuthenticationArgs.UserName, cmdLine.AuthenticationArgs.Password, dacpacName))
                {
                    var m = new LogMsg()
                    {
                        Message = $"Error creating the Platinum dacpac from {cmdLine.DacPacArgs.PlatinumServerSource} : {cmdLine.DacPacArgs.PlatinumDbSource}",
                        LogType = LogType.Error
                    };
                    WriteToLog(m);
                    return((int)ExecutionReturn.BuildFileExtractionError, cmdLine);
                }
                cmdLine.DacPacArgs.PlatinumDacpac        = dacpacName;
                ThreadedExecution.platinumDacPacFileName = dacpacName;
            }

            int tmpValReturn;

            //Check for the platinum dacpac and configure it if necessary
            (tmpValReturn, cmdLine) = Validation.ValidateAndLoadPlatinumDacpac(cmdLine, multiData);
            if (tmpValReturn == 0 && string.IsNullOrEmpty(ThreadedExecution.buildZipFileName))
            {
                ThreadedExecution.buildZipFileName = cmdLine.BuildFileName;
            }
            else if (tmpValReturn == (int)ExecutionReturn.DacpacDatabasesInSync)
            {
                return((int)ExecutionReturn.DacpacDatabasesInSync, cmdLine);
            }
            else if (tmpValReturn != 0)
            {
                return(tmpValReturn, cmdLine);
            }

            return(0, cmdLine);
        }
Esempio n. 6
0
        public async Task <(int retval, string readOnlySas)> StartBatch(bool stream = false, bool unittest = false)
        {
            string applicationPackage = string.Empty;

            if (string.IsNullOrWhiteSpace(cmdLine.BatchArgs.ApplicationPackage))
            {
                switch (cmdLine.BatchArgs.BatchPoolOs)
                {
                case OsType.Linux:
                    applicationPackage = "SqlBuildManagerLinux";
                    break;

                case OsType.Windows:
                default:
                    applicationPackage = "SqlBuildManagerWindows";
                    break;
                }
            }
            else
            {
                applicationPackage = cmdLine.BatchArgs.ApplicationPackage;
            }

            int cmdValid = ValidateBatchArgs(cmdLine, batchType);

            if (cmdValid != 0)
            {
                return(cmdValid, string.Empty);
            }


            //if extracting scripts from a platinum copy.. create the DACPAC here
            if (!string.IsNullOrWhiteSpace(cmdLine.DacPacArgs.PlatinumDbSource) && !string.IsNullOrWhiteSpace(cmdLine.DacPacArgs.PlatinumServerSource)) //using a platinum database as the source
            {
                log.LogInformation($"Extracting Platinum Dacpac from {cmdLine.DacPacArgs.PlatinumServerSource} : {cmdLine.DacPacArgs.PlatinumDbSource}");
                string dacpacName = Path.Combine(cmdLine.RootLoggingPath, cmdLine.DacPacArgs.PlatinumDbSource + ".dacpac");

                if (!DacPacHelper.ExtractDacPac(cmdLine.DacPacArgs.PlatinumDbSource, cmdLine.DacPacArgs.PlatinumServerSource, cmdLine.AuthenticationArgs.AuthenticationType, cmdLine.AuthenticationArgs.UserName, cmdLine.AuthenticationArgs.Password, dacpacName))
                {
                    log.LogError($"Error creating the Platinum dacpac from {cmdLine.DacPacArgs.PlatinumServerSource} : {cmdLine.DacPacArgs.PlatinumDbSource}");
                }
                cmdLine.DacPacArgs.PlatinumDacpac = dacpacName;
            }

            //Check for the platinum dacpac and configure it if necessary
            log.LogInformation("Validating database overrides");
            MultiDbData multiData;
            int?        myExitCode = 0;

            int tmpReturn = 0;

            //TODO: fix this for queue!!!!
            //Validate the override settings (not needed if --servicebusconnection is provided
            string[] errorMessages;
            int      tmpVal = Validation.ValidateAndLoadMultiDbData(cmdLine.MultiDbRunConfigFileName, cmdLine, out multiData, out errorMessages);

            if (tmpVal != 0)
            {
                log.LogError($"Unable to validate database config\r\n{string.Join("\r\n", errorMessages)}");
                return(tmpVal, string.Empty);
            }


            //Validate the platinum dacpac
            var tmpValReturn = Validation.ValidateAndLoadPlatinumDacpac(cmdLine, multiData);

            if (tmpValReturn.Item1 == (int)ExecutionReturn.DacpacDatabasesInSync)
            {
                return((int)ExecutionReturn.DacpacDatabasesInSync, string.Empty);
            }
            else if (tmpReturn != 0)
            {
                return(tmpValReturn.Item1, string.Empty);
            }

            BatchClient batchClient = null;

            //Get the batch and storage values
            string jobId, poolId, storageContainerName;

            (jobId, poolId, storageContainerName) = SetBatchJobAndStorageNames(cmdLine);
            log.LogInformation($"Using Azure Batch account: {cmdLine.ConnectionArgs.BatchAccountName} ({cmdLine.ConnectionArgs.BatchAccountUrl})");
            log.LogInformation($"Setting job id to: {jobId}");

            string readOnlySasToken = string.Empty;

            try
            {
                log.LogInformation($"Batch job start: {DateTime.Now}");
                Stopwatch timer = new Stopwatch();
                timer.Start();

                //Get storage ready
                BlobServiceClient          storageSvcClient = StorageManager.CreateStorageClient(cmdLine.ConnectionArgs.StorageAccountName, cmdLine.ConnectionArgs.StorageAccountKey);
                StorageSharedKeyCredential storageCreds     = new StorageSharedKeyCredential(cmdLine.ConnectionArgs.StorageAccountName, cmdLine.ConnectionArgs.StorageAccountKey);
                string containerSasToken = StorageManager.GetOutputContainerSasUrl(cmdLine.ConnectionArgs.StorageAccountName, storageContainerName, storageCreds, false);
                log.LogDebug($"Output write SAS token: {containerSasToken}");


                // Get a Batch client using account creds, and create the pool
                BatchSharedKeyCredentials cred = new BatchSharedKeyCredentials(cmdLine.ConnectionArgs.BatchAccountUrl, cmdLine.ConnectionArgs.BatchAccountName, cmdLine.ConnectionArgs.BatchAccountKey);
                batchClient = BatchClient.Open(cred);

                // Create a Batch pool, VM configuration, Windows Server image
                //bool success = CreateBatchPoolLegacy(batchClient, poolId, cmdLine.BatchArgs.BatchNodeCount,cmdLine.BatchArgs.BatchVmSize,cmdLine.BatchArgs.BatchPoolOs);
                bool success = await CreateBatchPool(cmdLine, poolId);


                // The collection of data files that are to be processed by the tasks
                List <string> inputFilePaths = new List <string>();
                if (!string.IsNullOrEmpty(cmdLine.DacPacArgs.PlatinumDacpac))
                {
                    inputFilePaths.Add(cmdLine.DacPacArgs.PlatinumDacpac);
                }
                if (!string.IsNullOrEmpty(cmdLine.BuildFileName))
                {
                    inputFilePaths.Add(cmdLine.BuildFileName);
                }
                if (!string.IsNullOrEmpty(cmdLine.MultiDbRunConfigFileName))
                {
                    inputFilePaths.Add(cmdLine.MultiDbRunConfigFileName);
                }
                if (!string.IsNullOrEmpty(this.queryFile))
                {
                    inputFilePaths.Add(this.queryFile);
                }


                //Get the list of DB targets and distribute across batch count
                var splitTargets = new List <string[]>();
                if (string.IsNullOrEmpty(cmdLine.ConnectionArgs.ServiceBusTopicConnectionString))
                {
                    int valRet = Validation.ValidateAndLoadMultiDbData(cmdLine.MultiDbRunConfigFileName, null, out MultiDbData multiDb, out errorMessages);
                    List <IEnumerable <(string, List <DatabaseOverride>)> > concurrencyBuckets = null;
                    if (valRet == 0)
                    {
                        if (cmdLine.ConcurrencyType == ConcurrencyType.Count)
                        {
                            //If it's just by count.. split evenly by the number of nodes
                            concurrencyBuckets = Concurrency.ConcurrencyByType(multiDb, cmdLine.BatchArgs.BatchNodeCount, cmdLine.ConcurrencyType);
                        }
                        else
                        {
                            //splitting by server is a little trickier, but run it and see what it does...
                            concurrencyBuckets = Concurrency.ConcurrencyByType(multiDb, cmdLine.Concurrency, cmdLine.ConcurrencyType);
                        }

                        //If we end up with fewer splits, then reduce the node count...
                        if (concurrencyBuckets.Count() < cmdLine.BatchArgs.BatchNodeCount)
                        {
                            log.LogWarning($"NOTE! The number of targets ({concurrencyBuckets.Count()}) is less than the requested node count ({cmdLine.BatchArgs.BatchNodeCount}). Changing the pool node count to {concurrencyBuckets.Count()}");
                            cmdLine.BatchArgs.BatchNodeCount = concurrencyBuckets.Count();
                        }
                        else if (concurrencyBuckets.Count() > cmdLine.BatchArgs.BatchNodeCount) //need to do some consolidating
                        {
                            log.LogWarning($"NOTE! When splitting by {cmdLine.ConcurrencyType.ToString()}, the number of targets ({concurrencyBuckets.Count()}) is greater than the requested node count ({cmdLine.BatchArgs.BatchNodeCount}). Will consolidate to fit within the number of nodes");
                            concurrencyBuckets = Concurrency.RecombineServersToFixedBucketCount(multiDb, cmdLine.BatchArgs.BatchNodeCount);
                        }
                    }
                    else
                    {
                        throw new ArgumentException($"Error parsing database targets. {String.Join(Environment.NewLine, errorMessages)}");
                    }
                    splitTargets = Concurrency.ConvertBucketsToConfigLines(concurrencyBuckets);

                    //Write out each split file
                    string rootPath = Path.GetDirectoryName(cmdLine.MultiDbRunConfigFileName);
                    for (int i = 0; i < splitTargets.Count; i++)
                    {
                        var tmpName = Path.Combine(rootPath, string.Format(baseTargetFormat, i));
                        File.WriteAllLines(tmpName, splitTargets[i]);
                        inputFilePaths.Add(tmpName);
                    }
                }

                // Upload the data files to Azure Storage. This is the data that will be processed by each of the tasks that are
                // executed on the compute nodes within the pool.
                List <ResourceFile> inputFiles = new List <ResourceFile>();

                foreach (string filePath in inputFilePaths)
                {
                    inputFiles.Add(StorageManager.UploadFileToBatchContainer(cmdLine.ConnectionArgs.StorageAccountName, storageContainerName, storageCreds, filePath));
                }

                //Create the individual command lines for each node
                IList <string> commandLines = CompileCommandLines(cmdLine, inputFiles, containerSasToken, cmdLine.BatchArgs.BatchNodeCount, jobId, cmdLine.BatchArgs.BatchPoolOs, applicationPackage, this.batchType);
                foreach (var s in commandLines)
                {
                    log.LogDebug(s);
                }

                try
                {
                    // Create a Batch job
                    log.LogInformation($"Creating job [{jobId}]...");
                    CloudJob job = batchClient.JobOperations.CreateJob();
                    job.Id = jobId;
                    job.PoolInformation = new PoolInformation {
                        PoolId = poolId
                    };
                    job.Commit();
                }
                catch (BatchException be)
                {
                    // Accept the specific error code JobExists as that is expected if the job already exists
                    if (be.RequestInformation?.BatchError?.Code == BatchErrorCodeStrings.JobExists)
                    {
                        log.LogInformation($"The job {jobId} already existed when we tried to create it");
                    }
                    else
                    {
                        throw; // Any other exception is unexpected
                    }
                }

                // Create a collection to hold the tasks that we'll be adding to the job
                if (splitTargets.Count != 0)
                {
                    log.LogInformation($"Adding {splitTargets.Count} tasks to job [{jobId}]...");
                }
                else if (!string.IsNullOrWhiteSpace(cmdLine.ConnectionArgs.ServiceBusTopicConnectionString))
                {
                    log.LogInformation($"Adding tasks to job [{jobId}]...");
                }

                List <CloudTask> tasks = new List <CloudTask>();

                // Create each of the tasks to process on each node
                for (int i = 0; i < commandLines.Count; i++)
                {
                    string taskId          = String.Format($"Task{i}");
                    string taskCommandLine = commandLines[i];

                    CloudTask task = new CloudTask(taskId, taskCommandLine);
                    task.ResourceFiles = inputFiles;
                    task.ApplicationPackageReferences = new List <ApplicationPackageReference>
                    {
                        new ApplicationPackageReference {
                            ApplicationId = applicationPackage
                        }
                    };
                    task.OutputFiles = new List <OutputFile>
                    {
                        new OutputFile(
                            filePattern: @"../std*.txt",
                            destination: new OutputFileDestination(new OutputFileBlobContainerDestination(containerUrl: containerSasToken, path: taskId)),
                            uploadOptions: new OutputFileUploadOptions(uploadCondition: OutputFileUploadCondition.TaskCompletion))    //,

                        //new OutputFile(
                        //    filePattern: @"../wd/*",
                        //    destination: new OutputFileDestination(new OutputFileBlobContainerDestination(containerUrl: containerSasToken, path: $"{taskId}/wd")),
                        //    uploadOptions: new OutputFileUploadOptions(uploadCondition: OutputFileUploadCondition.TaskCompletion)),

                        //new OutputFile(
                        //    filePattern: @"../wd/working/*",
                        //    destination: new OutputFileDestination(new OutputFileBlobContainerDestination(containerUrl: containerSasToken, path: $"{taskId}/working")),
                        //    uploadOptions: new OutputFileUploadOptions(uploadCondition: OutputFileUploadCondition.TaskCompletion))//,

                        //new OutputFile(
                        //   filePattern: @"../*.cfg",
                        //   destination: new OutputFileDestination(new OutputFileBlobContainerDestination(containerUrl: containerSasToken)),
                        //   uploadOptions: new OutputFileUploadOptions(uploadCondition: OutputFileUploadCondition.TaskCompletion))
                    };

                    tasks.Add(task);
                }

                // Add all tasks to the job.
                batchClient.JobOperations.AddTask(jobId, tasks);

                // Monitor task success/failure, specifying a maximum amount of time to wait for the tasks to complete.
                TimeSpan timeout = TimeSpan.FromMinutes(30);
                log.LogInformation($"Monitoring all tasks for 'Completed' state, timeout in {timeout}...");

                if (this.BatchProcessStartedEvent != null)
                {
                    this.BatchProcessStartedEvent(this, new BatchMonitorEventArgs(this.cmdLine, stream, unittest));
                }

                IEnumerable <CloudTask> addedTasks = batchClient.JobOperations.ListTasks(jobId);

                batchClient.Utilities.CreateTaskStateMonitor().WaitAll(addedTasks, TaskState.Completed, timeout);

                if (this.BatchExecutionCompletedEvent != null)
                {
                    this.BatchExecutionCompletedEvent(this, new BatchMonitorEventArgs(this.cmdLine, stream, unittest));
                }

                log.LogInformation("All tasks reached state Completed.");

                // Print task output
                log.LogInformation("Printing task output...");

                IEnumerable <CloudTask> completedtasks = batchClient.JobOperations.ListTasks(jobId);

                foreach (CloudTask task in completedtasks)
                {
                    string nodeId = String.Format(task.ComputeNodeInformation.ComputeNodeId);
                    log.LogInformation("---------------------------------");
                    log.LogInformation($"Task: {task.Id}");
                    log.LogInformation($"Node: {nodeId}");
                    log.LogInformation($"Exit Code: {task.ExecutionInformation.ExitCode}");
                    if (isDebug)
                    {
                        log.LogDebug("Standard out:");
                        log.LogDebug(task.GetNodeFile(Constants.StandardOutFileName).ReadAsString());
                    }
                    if (task.ExecutionInformation.ExitCode != 0)
                    {
                        myExitCode = task.ExecutionInformation.ExitCode;
                    }
                }
                log.LogInformation("---------------------------------");

                // Print out some timing info
                timer.Stop();
                log.LogInformation($"Batch job end: {DateTime.Now}");
                log.LogInformation($"Elapsed time: {timer.Elapsed}");


                // Clean up Batch resources
                if (cmdLine.BatchArgs.DeleteBatchJob)
                {
                    batchClient.JobOperations.DeleteJob(jobId);
                }
                if (cmdLine.BatchArgs.DeleteBatchPool)
                {
                    batchClient.PoolOperations.DeletePool(poolId);
                }

                SqlBuildManager.Logging.Threaded.Configure.CloseAndFlushAllLoggers();
                log.LogInformation("Consolidating log files");
                StorageManager.ConsolidateLogFiles(storageSvcClient, storageContainerName, inputFilePaths);

                if (batchType == BatchType.Query)
                {
                    StorageManager.CombineQueryOutputfiles(storageSvcClient, storageContainerName, this.outputFile);
                }

                //Finish the job out
                if (myExitCode == 0)
                {
                    log.LogInformation($"Setting job {jobId} status to Finished");
                    CloudJob j = batchClient.JobOperations.GetJob(jobId);
                    j.Terminate("Finished");
                }
                else
                {
                    log.LogInformation($"Setting job {jobId} status to exit code: {myExitCode}");
                    CloudJob j = batchClient.JobOperations.GetJob(jobId);
                    j.Terminate("Error");
                }


                readOnlySasToken = StorageManager.GetOutputContainerSasUrl(cmdLine.ConnectionArgs.StorageAccountName, storageContainerName, storageCreds, true);
                log.LogInformation($"Log files can be found here: {readOnlySasToken}");
                log.LogInformation("The read-only SAS token URL is valid for 7 days.");
                log.LogInformation("You can download \"Azure Storage Explorer\" from here: https://azure.microsoft.com/en-us/features/storage-explorer/");
                log.LogInformation("You can also get details on your Azure Batch execution from the \"Azure Batch Explorer\" found here: https://azure.github.io/BatchExplorer/");
            }
            catch (Exception exe)
            {
                log.LogError($"Exception when running batch job\r\n{exe.ToString()}");
                log.LogInformation($"Setting job {jobId} status to Failed");
                try
                {
                    CloudJob j = batchClient.JobOperations.GetJob(jobId);
                    j.Terminate("Failed");
                }
                catch { }
                myExitCode = 486;
            }
            finally
            {
                log.LogInformation("Batch complete");
                if (batchClient != null)
                {
                    batchClient.Dispose();
                }
            }

            if (myExitCode.HasValue)
            {
                log.LogInformation($"Exit Code: {myExitCode.Value}");
                return(myExitCode.Value, readOnlySasToken);
            }
            else
            {
                log.LogInformation($"Exit Code: {-100009}");
                return(-100009, readOnlySasToken);
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Performs the database scripts execution against the specified server/ database settings
        /// </summary>
        /// <param name="serverName">Name of the SQL server to target</param>
        /// <param name="overrides">List of database override settings to use in execution</param>
        /// <returns></returns>
        internal async Task <int> RunDatabaseBuild()
        {
            this.returnValue = (int)RunnerReturn.BuildResultInconclusive;

            ConnectionData   connData         = null;
            BackgroundWorker bg               = null;
            DoWorkEventArgs  e                = null;
            SqlBuildRunData  runData          = new SqlBuildRunData();
            string           targetDatabase   = overrides[0].OverrideDbTarget;
            string           loggingDirectory = Path.Combine(ThreadedExecution.WorkingDirectory, server, targetDatabase);

            try
            {
                //Start setting properties on the object that contains the run configuration data.
                runData.BuildType = "Other";
                if (!string.IsNullOrEmpty(cmdArgs.Description))
                {
                    runData.BuildDescription = cmdArgs.Description;
                }
                else
                {
                    runData.BuildDescription = "Threaded Multi-Database. Run ID:" + ThreadedExecution.RunID;
                }

                runData.IsTrial                 = this.isTrial;
                runData.RunScriptOnly           = false;
                runData.TargetDatabaseOverrides = overrides;
                runData.Server          = this.server;
                runData.IsTransactional = cmdArgs.Transactional;
                if (this.cmdArgs.LogToDatabaseName.Length > 0)
                {
                    runData.LogToDatabaseName = this.cmdArgs.LogToDatabaseName;
                }

                runData.PlatinumDacPacFileName = cmdArgs.DacPacArgs.PlatinumDacpac;
                runData.BuildRevision          = cmdArgs.BuildRevision;
                runData.DefaultScriptTimeout   = cmdArgs.DefaultScriptTimeout;
                runData.AllowObjectDelete      = cmdArgs.AllowObjectDelete;


                //Initilize the logging directory for this run
                if (!Directory.Exists(loggingDirectory))
                {
                    Directory.CreateDirectory(loggingDirectory);
                }

                if (forceCustomDacpac)
                {
                    runData.ForceCustomDacpac = true;
                    //This will set the BuildData and BuildFileName and ProjectFileName properties on runData
                    var status = DacPacHelper.UpdateBuildRunDataForDacPacSync(ref runData, server, targetDatabase, this.authType, this.username, this.password, loggingDirectory, cmdArgs.BuildRevision, cmdArgs.DefaultScriptTimeout, cmdArgs.AllowObjectDelete);
                    switch (status)
                    {
                    case DacpacDeltasStatus.Success:
                        //nothing to do
                        break;

                    case DacpacDeltasStatus.InSync:
                    case DacpacDeltasStatus.OnlyPostDeployment:
                        log.LogInformation($"Target database {targetDatabase} is already in sync with {cmdArgs.DacPacArgs.PlatinumDacpac}. Nothing to do!");
                        this.returnValue = (int)RunnerReturn.DacpacDatabasesInSync;
                        break;

                    default:
                        log.LogError($"Error creating custom dacpac and scripts for {targetDatabase}. No update was performed");
                        this.returnValue = (int)RunnerReturn.PackageCreationError;
                        return((int)RunnerReturn.PackageCreationError);;
                    }
                }
                else
                {
                    runData.ForceCustomDacpac = false;
                    //Get a full copy of the build data to work with (avoid threading sync issues)
                    SqlSyncBuildData buildData = new SqlSyncBuildData();

                    string xml = "<?xml version=\"1.0\" standalone=\"yes\"?>\r\n" + ThreadedExecution.BuildData.GetXml();
                    using (StringReader sr = new StringReader(xml))
                    {
                        buildData.ReadXml(sr);
                    }
                    //Clear out any existing ComittedScript data.. just log what is relevent to this run.
                    buildData.CommittedScript.Clear();


                    runData.BuildData       = buildData;
                    runData.ProjectFileName = Path.Combine(loggingDirectory, Path.GetFileName(ThreadedExecution.ProjectFileName));
                    runData.BuildFileName   = ThreadedExecution.BuildZipFileName;
                }



                //Create a connection object.. all we need is the server here, the DB will be filled in at execution time
                connData = new ConnectionData(server, "");
                if (this.cmdArgs.AuthenticationArgs.UserName.Length > 0 && this.cmdArgs.AuthenticationArgs.Password.Length > 0)
                {
                    connData.UserId   = cmdArgs.AuthenticationArgs.UserName;
                    connData.Password = cmdArgs.AuthenticationArgs.Password;
                }
                connData.AuthenticationType = this.cmdArgs.AuthenticationArgs.AuthenticationType;

                //Set the log file name
                string logFile = Path.Combine(loggingDirectory, "ExecutionLog.log");

                //Create the objects that will handle the event communication back.
                bg = new BackgroundWorker();
                //bg.ProgressChanged += Bg_ProgressChanged;
                bg.WorkerReportsProgress = true;
                e = new DoWorkEventArgs(null);
            }
            catch (Exception exe)
            {
                log.LogError(exe, $"Error Initializing run for {this.TargetTag}");
                WriteErrorLog(loggingDirectory, exe.ToString());
                this.returnValue = (int)ExecutionReturn.RunInitializationError;
                return((int)ExecutionReturn.RunInitializationError);;
            }

            log.LogDebug("Initializing run for " + this.TargetTag + ". Starting \"ProcessBuild\"");

            try
            {
                //Initilize the run helper object and kick it off.
                SqlBuildHelper helper = new SqlBuildHelper(connData, true, string.Empty, cmdArgs.Transactional); //don't need an "external" log for this, it's all external!
                helper.BuildCommittedEvent              += new BuildCommittedEventHandler(helper_BuildCommittedEvent);
                helper.BuildErrorRollBackEvent          += new EventHandler(helper_BuildErrorRollBackEvent);
                helper.BuildSuccessTrialRolledBackEvent += new EventHandler(helper_BuildSuccessTrialRolledBackEvent);
                await Task.Run(() =>
                {
                    helper.ProcessBuild(runData, bg, e, ThreadedExecution.BatchColl, this.buildRequestedBy, cmdArgs.TimeoutRetryCount);
                });
            }
            catch (Exception exe)
            {
                log.LogError("Error Processing run for " + this.TargetTag, exe);
                WriteErrorLog(loggingDirectory, exe.ToString());
                this.returnValue = (int)ExecutionReturn.ProcessBuildError;
                return((int)ExecutionReturn.ProcessBuildError);;
            }
            return(0);
        }