Пример #1
0
        internal static void UpdateStandAloneClusterForRemoveNode(string targetJsonFilePath, StandAloneCluster existingCluster)
        {
            StandAloneInstallerJsonModelBase targetJsonConfig = StandAloneInstallerJsonModelBase.GetJsonConfigFromFile(Path.Combine(Utility.TestDirectory, targetJsonFilePath));
            StandaloneSettingsValidator      validator        = new StandaloneSettingsValidator(targetJsonConfig);

            existingCluster.TargetCsmConfig = validator.ClusterProperties;
            existingCluster.Topology        = validator.Topology;
            List <NodeStatus> nodeStatus = new List <NodeStatus>();

            foreach (var node in existingCluster.Current.NodeConfig.NodesStatus)
            {
                NodeStatus ns = new NodeStatus();
                ns.NodeName  = node.NodeName;
                ns.NodeState = targetJsonConfig.Nodes.Select(n => n.NodeName).Contains(node.NodeName) ? NodeState.Enabled : NodeState.Removed;
                ns.NodeDeactivationIntent = targetJsonConfig.Nodes.Select(n => n.NodeName).Contains(node.NodeName) ? WrpNodeDeactivationIntent.Invalid : WrpNodeDeactivationIntent.RemoveNode;
                ns.NodeType   = node.NodeType;
                ns.InstanceId = 0;
                nodeStatus.Add(ns);
            }

            existingCluster.TargetNodeConfig = new ClusterNodeConfig
            {
                NodesStatus = nodeStatus,
                Version     = 3
            };
        }
        internal void InternalValidateClusterCertUpdateTest(
            string originalThumbprint,
            string originalSecondaryThumbprint,
            string updatedThumbprint,
            string updatedSecondaryThumbprint,
            ClusterManagementErrorCode?expectedErrorCode = null)
        {
            CertificateDescription currentCert = new CertificateDescription()
            {
                Thumbprint          = originalThumbprint,
                ThumbprintSecondary = originalSecondaryThumbprint,
            };

            CertificateDescription updatedCert = new CertificateDescription()
            {
                Thumbprint          = updatedThumbprint,
                ThumbprintSecondary = updatedSecondaryThumbprint,
            };

            Utility.ValidateExpectedValidationException(
                delegate
            {
                StandaloneSettingsValidator.ValidateClusterCertificateUpdate(
                    new X509()
                {
                    ClusterCertificate = currentCert
                },
                    new X509()
                {
                    ClusterCertificate = updatedCert
                },
                    new StandAloneTraceLogger("StandAloneDeploymentManager"));
            },
                expectedErrorCode);
        }
Пример #3
0
        internal static bool IsJsonConfigModelValid(StandAloneInstallerJsonModelBase config, StandAloneInstallerJsonModelBase oldConfig, bool validateDownNodes, bool throwIfError = false)
        {
            try
            {
                config.ThrowValidationExceptionIfNull(StringResources.Error_BPAJsonModelInvalid);
                config.ValidateModel();
                var settingsValidator = new StandaloneSettingsValidator(config);
                settingsValidator.Validate(validateDownNodes);

                // UOS validates upgrade config diffs by calling ValidateUpdateFrom() method directly. Test-Configuration invokes IsJsonConfigModelValid() and it calls ValidateUpdateFrom inside.
                // The reason is from Test-Configuration, there is no cluster connection, so that we ignore ValidateTopologyAsync(). Therefore in the ValidateUpdateFrom here there is no need
                // to await the async call. However in UOS, we should call ValidateUpdateFrom in an async manner. That's why I am not trying to have the UOS/TestConfiguration going down the same path.
                if (oldConfig != null)
                {
                    settingsValidator.ValidateUpdateFrom(oldConfig.GetUserConfig(), oldConfig.GetClusterTopology(), connectedToCluster: false).GetAwaiter().GetResult();
                }
            }
            catch (FileNotFoundException ex)
            {
                SFDeployerTrace.WriteError(StringResources.Error_BPAPackageFileNotFound, ex.ToString());
                return(false);
            }
            catch (ValidationException ex)
            {
                SFDeployerTrace.WriteError(StringResources.Error_BPAModelSettingsValidationFailed, ex.GetMessage(System.Globalization.CultureInfo.InvariantCulture));
                if (throwIfError)
                {
                    throw;
                }

                return(false);
            }

            return(true);
        }
        internal void InternalValidateClusterThumbprintUpdateTest(
            string originalThumbprint,
            string originalSecondaryThumbprint,
            string updatedThumbprint,
            string updatedSecondaryThumbprint,
            ClusterManagementErrorCode?expectedErrorCode = null)
        {
            CertificateDescription currentCert = new CertificateDescription()
            {
                Thumbprint          = originalThumbprint,
                ThumbprintSecondary = originalSecondaryThumbprint,
            };

            CertificateDescription updatedCert = new CertificateDescription()
            {
                Thumbprint          = updatedThumbprint,
                ThumbprintSecondary = updatedSecondaryThumbprint,
            };

            Utility.ValidateExpectedValidationException(
                delegate
            {
                StandaloneSettingsValidator.ValidateClusterCertificateThumbprintAndCnUpdate(
                    StandaloneSettingsValidator.GetUniqueThumbprints(currentCert),
                    StandaloneSettingsValidator.GetUniqueThumbprints(updatedCert),
                    true,
                    true);
            },
                expectedErrorCode);
        }
        internal void InternalGetUniqueThumbprintsTest(CertificateDescription cert, int expectedThumbprints)
        {
            IEnumerable <string> thumbprints = StandaloneSettingsValidator.GetUniqueThumbprints(cert);

            Assert.AreEqual(expectedThumbprints, thumbprints.Count());
            Assert.IsTrue(thumbprints.Contains(cert.Thumbprint));
            if (expectedThumbprints > 1)
            {
                Assert.IsTrue(thumbprints.Contains(cert.ThumbprintSecondary));
            }
        }
 internal void InternalValidateClusterCertificateIssuerStoreUpdateTest(List <CertificateIssuerStore> originalClusterIssuerStore, List <CertificateIssuerStore> updatedClusterIssuerStore, ClusterManagementErrorCode?expectedErrorCode = null)
 {
     Utility.ValidateExpectedValidationException(
         delegate
     {
         StandaloneSettingsValidator.ValidateClusterIssuerStoreUpdate(
             originalClusterIssuerStore,
             updatedClusterIssuerStore,
             new StandAloneTraceLogger("StandAloneDeploymentManager"));
     },
         expectedErrorCode);
 }
 internal void InternalValidateAtLeastOneCertInstalledOnOneNodeTest(IEnumerable <string> thumbprints, ClusterManagementErrorCode?expectedErrorCode)
 {
     Utility.ValidateExpectedValidationException(
         delegate
     {
         StandaloneSettingsValidator.ValidateAtLeastOneCertInstalledOnCurrentNode(
             thumbprints,
             StoreName.My,
             X509FindType.FindByThumbprint,
             new StandAloneTraceLogger("StandAloneDeploymentManager"));
     },
         expectedErrorCode);
 }
 internal void InternalValidateAllCertsInstalledOnAllNodesTest(IEnumerable <string> thumbprints, ClusterManagementErrorCode?expectedErrorCode)
 {
     Utility.ValidateExpectedValidationException(
         delegate
     {
         StandaloneSettingsValidator.ValidateAllCertsInstalledOnAllNodes(
             thumbprints,
             StoreName.My,
             X509FindType.FindByThumbprint,
             new string[] { "localhost" },
             new StandAloneTraceLogger("StandAloneDeploymentManager"));
     },
         expectedErrorCode);
 }
        internal void InternalValidateClusterCertificateIssuerThumbprintUpdateTest(string originalCns, string originalIssuerThumbprints, string updatedCns, string updatedIssuerThumbprints, ClusterManagementErrorCode?expectedErrorCode = null)
        {
            Utility.ValidateExpectedValidationException(
                delegate
            {
                ServerCertificateCommonNames original = ConstructServerCns(originalCns, originalIssuerThumbprints);
                ServerCertificateCommonNames updated  = ConstructServerCns(updatedCns, updatedIssuerThumbprints);

                StandaloneSettingsValidator.ValidateClusterCertificateIssuerThumbprintUpdate(
                    original,
                    updated,
                    new StandAloneTraceLogger("StandAloneDeploymentManager"));
            },
                expectedErrorCode);
        }
Пример #10
0
        internal static void UpdateStandAloneCluster(string targetJsonFilePath, StandAloneCluster existingCluster, bool isUserSet = false)
        {
            StandAloneInstallerJsonModelBase targetJsonConfig = StandAloneInstallerJsonModelBase.GetJsonConfigFromFile(Path.Combine(Utility.TestDirectory, targetJsonFilePath));
            StandaloneSettingsValidator      validator        = new StandaloneSettingsValidator(targetJsonConfig);

            existingCluster.TargetCsmConfig = validator.ClusterProperties;
            existingCluster.Topology        = validator.Topology;

            if (!string.IsNullOrEmpty(validator.ClusterProperties.CodeVersion))
            {
                var adminConfigVersion = new StandaloneAdminConfig(null, isUserSet);
                adminConfigVersion.Version.MsiVersion = validator.ClusterProperties.CodeVersion;
                existingCluster.TargetWrpConfig       = adminConfigVersion;
            }
        }
Пример #11
0
        public void DeploymentManagerTest_InvalidJsonConfigWindowsGMSAOct2017()
        {
            string clusterConfigPath = Path.Combine(BaseDir, JsonConfigWindowsGMSAInvalidOct2017Filename);
            var    configModel       = StandAloneInstallerJsonModelBase.GetJsonConfigFromFile(clusterConfigPath);

            Assert.IsTrue(File.Exists(clusterConfigPath), string.Format("Config file {0} did not exist.", clusterConfigPath));
            Assert.IsNotNull(configModel, "JSON config was invalid.");

            var settingsValidator = new StandaloneSettingsValidator(configModel);

            Utility.ValidateExpectedValidationException(
                delegate
            {
                settingsValidator.Validate(false);
            },
                ClusterManagementErrorCode.BestPracticesAnalyzerModelInvalid);
        }
        public void ValidateUpgradeTest()
        {
            // Windows/ GMSA security <-> unsecure is not allowed.
            StandAloneInstallerJSONModelAugust2017 v1model   = Utility.CreateMockupStandAloneJsonModel("testCluster", "1.0.0", security: Utility.CreateGMSASecurity());
            StandAloneInstallerJSONModelAugust2017 v2model   = Utility.CreateMockupStandAloneJsonModel("testCluster", "2.0.0");
            StandaloneSettingsValidator            validator = new StandaloneSettingsValidator(v2model);

            Utility.ValidateExpectedValidationException(
                delegate
            {
                validator.ValidateUpdateFrom(v1model.GetUserConfig(), v1model.GetClusterTopology(), false).GetAwaiter().GetResult();
            },
                ClusterManagementErrorCode.AuthenticationTypeCannotBeChangedFromWindowsToUnsecured);

            // For the same nodename, node IP can't be changed.
            List <NodeDescriptionGA> nodelist   = new List <NodeDescriptionGA>();
            NodeDescriptionGA        node       = Utility.CreateNodeDescription("node1", "NodeType0", "machine1", "fd:/dc1/r0", "UD0");
            List <NodeDescriptionGA> nodelistV2 = new List <NodeDescriptionGA>();
            NodeDescriptionGA        nodeV2     = Utility.CreateNodeDescription("node1", "NodeType0", "machine2", "fd:/dc1/r0", "UD0");

            nodelist.Add(node);
            nodelistV2.Add(nodeV2);
            v1model   = Utility.CreateMockupStandAloneJsonModel("testCluster", "1.0.0", nodes: nodelist);
            v2model   = Utility.CreateMockupStandAloneJsonModel("testCluster", "2.0.0", nodes: nodelistV2);
            validator = new StandaloneSettingsValidator(v2model);

            Utility.ValidateExpectedValidationException(
                delegate
            {
                validator.ValidateUpdateFrom(v1model.GetUserConfig(), v1model.GetClusterTopology(), false).GetAwaiter().GetResult();
            },
                ClusterManagementErrorCode.NodeIPNodeTypeRefCantBeChanged);

            // Reverse proxy port change is allowed.
            v1model = Utility.CreateMockupStandAloneJsonModel("testCluster", "1.0.0");
            v2model = Utility.CreateMockupStandAloneJsonModel("testCluster", "2.0.0");
            foreach (var currentNodeType in v2model.Properties.NodeTypes)
            {
                currentNodeType.ReverseProxyEndpointPort = 19081;
            }
            validator = new StandaloneSettingsValidator(v2model);
            validator.ValidateUpdateFrom(v1model.GetUserConfig(), v1model.GetClusterTopology(), false).GetAwaiter().GetResult();
        }
        private async Task UpdatePersistedCodeUpgradePackage(StandaloneSettingsValidator validator)
        {
            bool codeVersionChanged = await validator.IsCodeVersionChangedAsync(validator.ClusterProperties.CodeVersion).ConfigureAwait(false);

            if (!codeVersionChanged || validator.ClusterProperties.CodeVersion == DMConstants.AutoupgradeCodeVersion)
            {
                return;
            }

            UpgradeOrchestrationTrace.TraceSource.WriteInfo(TraceType, "Found a different version, updating persistence state with version: {0}", validator.ClusterProperties.CodeVersion);

            CodeUpgradeDetail packageDetails = new CodeUpgradeDetail()
            {
                CodeVersion     = validator.ClusterProperties.CodeVersion,
                IsUserInitiated = true
            };

            await this.storeManager.PersistCodeUpgradePackageDetailsAsync(Constants.CodeUpgradePackageDetails, packageDetails, this.cancellationToken);
        }
        internal void InternalValidateClusterCnUpdateTest(
            string originalCns,
            string updatedCns,
            ClusterManagementErrorCode?expectedErrorCode = null)
        {
            Utility.ValidateExpectedValidationException(
                delegate
            {
                X509 originalSecurity = new X509()
                {
                    ClusterCertificateCommonNames = new ServerCertificateCommonNames()
                    {
                        CommonNames = new List <CertificateCommonNameBase>(originalCns.Split(',').ToList().Select(p => new CertificateCommonNameBase()
                        {
                            CertificateCommonName = p
                        }))
                    }
                };
                X509 updatedSecurity = new X509()
                {
                    ClusterCertificateCommonNames = new ServerCertificateCommonNames()
                    {
                        CommonNames = new List <CertificateCommonNameBase>(updatedCns.Split(',').ToList().Select(p => new CertificateCommonNameBase()
                        {
                            CertificateCommonName = p
                        }))
                    }
                };

                List <string> originalThumbprintsOrCns = StandaloneSettingsValidator.GetClusterCertUniqueThumbprintsOrCommonNames(originalSecurity);
                List <string> updatedThumbprintsOrCns  = StandaloneSettingsValidator.GetClusterCertUniqueThumbprintsOrCommonNames(updatedSecurity);

                StandaloneSettingsValidator.ValidateClusterCertificateThumbprintAndCnUpdate(
                    originalThumbprintsOrCns,
                    updatedThumbprintsOrCns,
                    false,
                    false);
            },
                expectedErrorCode);
        }
 internal void InternalValidateRepairManagerUpgradeTest(IUserConfig[] currentCfgs, IUserConfig[] newCfgs, bool shouldPass)
 {
     foreach (IUserConfig currentCfg in currentCfgs)
     {
         foreach (IUserConfig newCfg in newCfgs)
         {
             try
             {
                 StandaloneSettingsValidator.ValidateRepairManager(currentCfg, newCfg);
                 if (!shouldPass)
                 {
                     throw new Exception("test failed!");
                 }
             }
             catch (ValidationException ex)
             {
                 if (shouldPass)
                 {
                     throw new Exception("test failed!", ex);
                 }
             }
         }
     }
 }
        public async Task ProcessStartClusterConfigurationUpgradeAsync(ConfigurationUpgradeDescription configUpgradeDesc, TimeSpan timeout, CancellationToken cancellationToken)
        {
            /* The cancellation token passed in this API call is not used (by design). This token corresponds to the client side call.
             * The global this.cancellationToken is initialised in RunAsync() and is honored in every API call. */

            UpgradeOrchestrationTrace.TraceSource.WriteInfo(TraceType, "Entering ProcessStartUpgradeAsync.");
            try
            {
                UpgradeOrchestrationTrace.TraceSource.WriteInfo(TraceType, "Deserializing input json config string.");
                StandAloneInstallerJsonModelBase targetJsonConfig = StandAloneInstallerJsonModelBase.GetJsonConfigFromString(configUpgradeDesc.ClusterConfiguration);
                if (targetJsonConfig == null)
                {
                    throw new ArgumentException("The input cluster configuration is not in a valid json format or supported apiVersion.");
                }

                UpgradeOrchestrationTrace.TraceSource.WriteInfo(TraceType, "Retrieve current cluster resource from StoreManager.");
                StandAloneCluster cluster = await this.storeManager.GetClusterResourceAsync(
                    Constants.ClusterReliableDictionaryKey, this.cancellationToken).ConfigureAwait(false);

                bool isInDataLossState = await DatalossHelper.IsInDatalossStateAsync(this.cancellationToken).ConfigureAwait(false);

                if (!isInDataLossState)
                {
                    if (cluster == null || cluster.Current == null)
                    {
                        UpgradeOrchestrationTrace.TraceSource.WriteWarning(TraceType, "Persisted cluster resource is not ready: {0}", cluster == null ? "null" : "current = null");
                        throw new FabricException("UpgradeOrchestrationService is not ready.");
                    }

                    UpgradeOrchestrationTrace.TraceSource.WriteInfo(TraceType, "Setting target config and topology based on new input json config.");

                    if (cluster.Pending != null && !this.IsInterruptibleAsync(cluster.Pending).Result)
                    {
                        throw new FabricException(string.Format("Cluster configuration upgrade of type {0} is already in progress and cannot be interrupted.", cluster.Pending.GetType().Name));
                    }

                    StandaloneSettingsValidator validator = new StandaloneSettingsValidator(targetJsonConfig);

                    await UpgradeOrchestrationMessageProcessor.ValidateModel(targetJsonConfig, validator, cluster, true).ConfigureAwait(false);

                    var removedNodes = validator.GetRemovedNodes(cluster.Topology);
                    var addedNodes   = validator.GetAddedNodes(cluster.Topology);

                    if (addedNodes.Any() && StandaloneUtility.CheckFabricRunningAsGMSA(cluster.Current.CSMConfig))
                    {
                        /* Need to resolve assembly so that FabricDeployer can load the right binaries from FabricCodePath since not all binaries required by FabricDeployer are present in UOS.Current folder.*/
                        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.LoadFromFabricCodePath);
                        try
                        {
                            await this.PerformAddNodeOperationGMSAAsync(addedNodes, this.fabricClient, validator.ClusterProperties.NodeTypes).ConfigureAwait(false);
                        }
                        catch (AggregateException ex)
                        {
                            UpgradeOrchestrationTrace.TraceSource.WriteError(TraceType, "Adding nodes for GMSA scenario failed with exception: {0}", ex);
                            throw;
                        }
                        finally
                        {
                            AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(this.LoadFromFabricCodePath);
                        }
                    }

                    if (addedNodes.Any())
                    {
                        cluster.TargetNodeConfig = GetTargetNodeConfigAddNode(validator.Topology, cluster.Current.NodeConfig.Version);
                    }

                    if (removedNodes.Any())
                    {
                        cluster.TargetNodeConfig = GetTargetNodeConfigRemoveNode(cluster.Topology, removedNodes, cluster.Current.NodeConfig.Version);
                    }
                    else
                    {
                        cluster.Topology = validator.Topology;
                    }

                    cluster.TargetCsmConfig = validator.ClusterProperties;

                    // Cluster is updated above so persist it.
                    await this.storeManager.PersistClusterResourceAsync(Constants.ClusterReliableDictionaryKey, cluster, cancellationToken).ConfigureAwait(false);

                    await this.UpdatePersistedCodeUpgradePackage(validator).ConfigureAwait(false);

                    UpgradeOrchestrationTrace.TraceSource.WriteInfo(TraceType, "Invoking Orchestrator");

                    await this.orchestrator.StartUpgradeAsync(cluster, this.cancellationToken, configUpgradeDesc).ContinueWith(t =>
                    {
                        if (t.Exception != null)
                        {
                            UpgradeOrchestrationTrace.TraceSource.WriteWarning(TraceType, "Orchestrator completed with status: {0} exception: {1}", t.Status, t.Exception);
                        }
                        else
                        {
                            UpgradeOrchestrationTrace.TraceSource.WriteInfo(TraceType, "Orchestrator completed with status: {0}", t.Status);
                        }
                    });
                }
                else
                {
                    StandaloneSettingsValidator validator = new StandaloneSettingsValidator(targetJsonConfig);
                    await UpgradeOrchestrationMessageProcessor.ValidateModel(targetJsonConfig, validator, cluster, false).ConfigureAwait(false);

                    cluster = FabricUpgradeOrchestrationService.ConstructClusterFromJson(targetJsonConfig, FabricNativeConfigStore.FabricGetConfigStore());

                    DatalossHelper.DryRunConfigUpgrade(cluster);
                    await this.storeManager.PersistClusterResourceAsync(Constants.ClusterReliableDictionaryKey, cluster, this.cancellationToken);

                    await DatalossHelper.UpdateHeathStateAsync(isHealthy : true).ConfigureAwait(false);
                }
            }
            catch (Exception e)
            {
                UpgradeOrchestrationTrace.TraceSource.WriteWarning(TraceType, "ProcessStartUpgradeAsync exception: {0}", e);
                throw UpgradeOrchestrationMessageProcessor.ConvertToComException(e);
            }

            UpgradeOrchestrationTrace.TraceSource.WriteInfo(TraceType, "Exiting ProcessStartUpgradeAsync.");
        }
        internal static async Task ValidateModel(StandAloneInstallerJsonModelBase targetJsonConfig, StandaloneSettingsValidator validator, StandAloneCluster cluster, bool validateUpgrade)
        {
            BestPracticesAnalyzer.IsJsonConfigModelValid(targetJsonConfig, oldConfig: null, validateDownNodes: false, throwIfError: true);

            if (validateUpgrade)
            {
                await validator.ValidateUpdateFrom(cluster.Current.CSMConfig, cluster.Topology, connectedToCluster : true).ConfigureAwait(false);
            }
        }
        public void ValidateTopologyTest()
        {
            // Node name contains invalid characters.
            List <NodeDescriptionGA> nodelist = new List <NodeDescriptionGA>();
            NodeDescriptionGA        node     = Utility.CreateNodeDescription("<", "nodetype1", "machine1", "fd:/dc1/r0", "UD0");

            nodelist.Add(node);
            StandAloneInstallerJSONModelAugust2017 v1model           = Utility.CreateMockupStandAloneJsonModel("testCluster", "1.0.0", nodes: nodelist);
            StandaloneSettingsValidator            settingsValidator = new StandaloneSettingsValidator(v1model);

            Utility.ValidateExpectedValidationException(
                delegate
            {
                settingsValidator.ValidateTopology();
            },
                ClusterManagementErrorCode.NodeNameContainsInvalidChars);

            // Duplicate node name.
            List <NodeDescriptionGA> nodelist2 = new List <NodeDescriptionGA>();
            NodeDescriptionGA        node1     = Utility.CreateNodeDescription("node1", "nodetype1", "machine1", "fd:/dc1/r0", "UD0");
            NodeDescriptionGA        node2     = Utility.CreateNodeDescription("node1", "nodetype1", "machine2", "fd:/dc1/r1", "UD1");

            nodelist2.Add(node1);
            nodelist2.Add(node2);
            v1model = Utility.CreateMockupStandAloneJsonModel("testCluster", "1.0.0", nodes: nodelist2);

            Utility.ValidateExpectedValidationException(
                delegate
            {
                settingsValidator = new StandaloneSettingsValidator(v1model);
            },
                ClusterManagementErrorCode.NodeNameDuplicateDetected);

            // FD count needs to be greater than 2 for multi-box deployment.
            List <NodeDescriptionGA> nodelist4 = new List <NodeDescriptionGA>();

            node1 = Utility.CreateNodeDescription("node1", "nodetype1", "machine1", "fd:/dc1/r0", "UD0");
            node2 = Utility.CreateNodeDescription("node2", "nodetype1", "machine2", "fd:/dc1/r0", "UD1");
            NodeDescriptionGA node3 = Utility.CreateNodeDescription("node3", "nodetype1", "machine3", "fd:/dc1/r0", "UD2");

            nodelist4.Add(node1);
            nodelist4.Add(node2);
            nodelist4.Add(node3);
            v1model           = Utility.CreateMockupStandAloneJsonModel("testCluster", "1.0.0", nodes: nodelist4);
            settingsValidator = new StandaloneSettingsValidator(v1model);

            Utility.ValidateExpectedValidationException(
                delegate
            {
                settingsValidator.ValidateTopology();
            },
                ClusterManagementErrorCode.FDMustBeGreaterThan2);

            // Scale-min should be blocked for multi-box deployment
            List <NodeDescriptionGA> nodelistScaleMin = new List <NodeDescriptionGA>();

            node1 = Utility.CreateNodeDescription("node1", "nodetype1", "machine1", "fd:/dc1/r0", "UD0");
            node2 = Utility.CreateNodeDescription("node2", "nodetype1", "machine1", "fd:/dc1/r1", "UD1");
            node3 = Utility.CreateNodeDescription("node3", "nodetype1", "machine2", "fd:/dc2/r1", "UD2");
            nodelistScaleMin.Add(node1);
            nodelistScaleMin.Add(node2);
            nodelistScaleMin.Add(node3);
            v1model           = Utility.CreateMockupStandAloneJsonModel("testCluster", "1.0.0", nodes: nodelistScaleMin);
            settingsValidator = new StandaloneSettingsValidator(v1model);

            Utility.ValidateExpectedValidationException(
                delegate
            {
                settingsValidator.ValidateTopology();
            },
                ClusterManagementErrorCode.ScaleMinNotAllowed);
        }
        internal void InternalValidateClusterCertificateInstallationTest(string[] findValues, string[] issuerThumbprints, string certType, ClusterManagementErrorCode?expectedErrorCode)
        {
            if (issuerThumbprints == null)
            {
                IEnumerable <CertificateDescription> certs = findValues.Select(p => new CertificateDescription()
                {
                    Thumbprint = p
                });
                foreach (CertificateDescription cert in certs)
                {
                    var x509Cert = new X509();
                    switch (certType)
                    {
                    case "Cluster":
                        x509Cert.ClusterCertificate = cert;
                        break;

                    case "Server":
                        x509Cert.ServerCertificate = cert;
                        break;

                    case "ReverseProxy":
                        x509Cert.ReverseProxyCertificate = cert;
                        break;
                    }

                    Utility.ValidateExpectedValidationException(
                        delegate
                    {
                        StandaloneSettingsValidator.ValidateCertificateInstallation(
                            x509Cert,
                            new string[] { "localhost" },
                            new StandAloneTraceLogger("StandAloneDeploymentManager"));
                    },
                        expectedErrorCode);
                }
            }
            else
            {
                List <ServerCertificateCommonNames> certs = new List <ServerCertificateCommonNames>();
                for (int i = 0; i < findValues.Count(); i++)
                {
                    ServerCertificateCommonNames cert = new ServerCertificateCommonNames()
                    {
                        CommonNames = new List <CertificateCommonNameBase>()
                    };
                    cert.CommonNames.Add(new CertificateCommonNameBase()
                    {
                        CertificateCommonName = findValues[i], CertificateIssuerThumbprint = issuerThumbprints[i]
                    });
                    certs.Add(cert);
                }

                foreach (ServerCertificateCommonNames cert in certs)
                {
                    var x509Cert = new X509();
                    switch (certType)
                    {
                    case "Cluster":
                        x509Cert.ClusterCertificateCommonNames = cert;
                        break;

                    case "Server":
                        x509Cert.ServerCertificateCommonNames = cert;
                        break;

                    case "ReverseProxy":
                        x509Cert.ReverseProxyCertificateCommonNames = cert;
                        break;
                    }

                    Utility.ValidateExpectedValidationException(
                        delegate
                    {
                        StandaloneSettingsValidator.ValidateCertificateInstallation(
                            x509Cert,
                            new string[] { "localhost" },
                            new StandAloneTraceLogger("StandAloneDeploymentManager"));
                    },
                        expectedErrorCode);
                }
            }
        }