public static bool TryLoadFromFile(string publicKeyFile, out PublicKeyCertificate publicKeyCertificate)
        {
            publicKeyCertificate = null;

            try
            {
                X509Certificate2 certificate = new X509Certificate2(publicKeyFile);

                if (certificate.HasPrivateKey)
                {
                    TextLogger.LogError("Certificate from public key file {0} should not present a private key.", publicKeyFile);

                    return(false);
                }

                publicKeyCertificate = new PublicKeyCertificate(certificate);

                return(true);
            }
            catch (Exception e)
            {
                TextLogger.LogError("Failed to load public key certificate from public key file {0} : {1}", publicKeyFile, e);

                return(false);
            }
        }
        public void StopLocalNode()
        {
            // AP service manager waits for ShutdownTimeoutInMilliseconds (by default 5s) before forcefully terminating the service host.
            // For now we are not waiting for Fabric host service to stop on stopping path.
            ServiceController fabricInstallerService = this.GetInstalledService(Constants.FabricInstallerServiceName);

            if (fabricInstallerService != null)
            {
                if (fabricInstallerService.Status != ServiceControllerStatus.StopPending && fabricInstallerService.Status != ServiceControllerStatus.Stopped)
                {
                    TextLogger.LogInfo("Stopping Fabric installer serivce.");

                    fabricInstallerService.Stop();
                }
            }

            ServiceController fabricHostService = this.GetInstalledService(Constants.FabricHostServiceName);

            if (fabricHostService != null)
            {
                if (fabricHostService.Status != ServiceControllerStatus.StopPending && fabricHostService.Status != ServiceControllerStatus.Stopped)
                {
                    TextLogger.LogInfo("Stopping Fabric host serivce.");

                    fabricHostService.Stop();
                }
            }
        }
Example #3
0
        /*
         * TODO: currently AP safe config deployment is not supported to update configurations.
         * The only way to update the configurations is via an application upgrade.
         */
        public static bool GetCurrentConfigurations()
        {
            IConfiguration configuration = APConfiguration.GetConfiguration();

            CertificateManagerRunIntervalSeconds = configuration.GetInt32Value(StringConstants.CertificateManagerConfigurationSectionName, "CertificateManagerRunIntervalSeconds", 3600);

            CertificateManagerRetryIntervalSeconds = configuration.GetInt32Value(StringConstants.CertificateManagerConfigurationSectionName, "CertificateManagerRetryIntervalSeconds", 300);

            EnableSecretStorePersistentCaching = configuration.GetBoolValue(StringConstants.CertificateManagerConfigurationSectionName, "EnableSecretStoreClientSidePersistentCaching", true);

            string logLevelString = configuration.GetStringValue(StringConstants.EnvironmentSyncConfigurationSectionName, "LogLevel", "Info");

            LogLevel logLevel;

            if (!Enum.TryParse <LogLevel>(logLevelString, out logLevel))
            {
                TextLogger.LogError("Log level is invalid : {0}.", logLevelString);

                return(false);
            }

            LogLevel = logLevel;

            return(true);
        }
        public bool ConfigureLocalNode(ClusterTopology topologyToDeploy)
        {
            try
            {
                TextLogger.LogInfo("Configuring local Service Fabric node based on bootstrap cluster manifest.");

                if (!this.InvokeFabricDeployer(topologyToDeploy, DeploymentType.Create, false))
                {
                    TextLogger.LogError("Failed to configure local Service Fabric node based on bootstrap cluster manifest.");

                    return(false);
                }

                Utilities.SetWindowsFabricNodeConfigurationCompleted(false);

                TextLogger.LogInfo("Successfully configured local Service Fabric node based on bootstrap cluster manifest.");

                return(true);
            }
            catch (Exception e)
            {
                TextLogger.LogError("Failed to configure local Service Fabric node based on bootstrap cluster manifest : {0}", e);

                return(false);
            }
        }
        public bool InstallProduct()
        {
            TextLogger.LogInfo("Installing Service Fabric product.");

            try
            {
                string installedServiceFabricProductVersion = Utilities.GetCurrentWindowsFabricVersion();

                if (installedServiceFabricProductVersion == null)
                {
                    /*
                     * For Service Fabric on Autopilot, xcopyable deployment is the only supported mechanism for installing Service Fabric product.
                     * MSI is not supported.
                     */
                    string serviceFabricCabFile = this.GetServiceFabricCabFile();

                    if (File.Exists(serviceFabricCabFile))
                    {
                        TextLogger.LogInfo("Installing Service Fabric product with cab file {0}.", serviceFabricCabFile);

                        string localInstallationPath = Environment.ExpandEnvironmentVariables(Constants.FabricRootPath);
                        if (!Directory.Exists(localInstallationPath))
                        {
                            Directory.CreateDirectory(localInstallationPath);
                        }

                        Utilities.UnCabFile(serviceFabricCabFile, localInstallationPath);

                        Utilities.SetFabricRoot(localInstallationPath);

                        Utilities.SetFabricCodePath(Path.Combine(localInstallationPath, Constants.PathToFabricCode));

                        // TODO: registry key not available yet
                        installedServiceFabricProductVersion = Utilities.GetCurrentWindowsFabricVersion();

                        TextLogger.LogInfo("Successfully installed Service Fabric product version {0} to local path {1} and set Service Fabric registry keys.", installedServiceFabricProductVersion, localInstallationPath);
                    }
                    else
                    {
                        // TODO: [General] surface fatal errors outside the agent as AP health report.
                        TextLogger.LogError("Failed to install Service Fabric product : Service Fabric product cab file does not exist in agent application directory as {0}.", serviceFabricCabFile);

                        return(false);
                    }
                }
                else
                {
                    TextLogger.LogInfo("Service Fabric product version {0} is already installed locally. Skip product installation.", installedServiceFabricProductVersion);
                }

                return(true);
            }
            catch (Exception e)
            {
                TextLogger.LogError("Failed to install Service Fabric product : {0}", e);

                return(false);
            }
        }
        private void ConsoleCancelEventHanlder(object sender, ConsoleCancelEventArgs args)
        {
            TextLogger.LogInfo("Cancel event (Ctrl + C / Ctrl + Break) received. Stopping Service Fabric Environment Sync.");

            this.certificateManagerCancellationTokenSource.Cancel();

            TextLogger.LogInfo("Service Fabric Environment Sync has stopped. Exiting the process.");

            this.Exit(true);
        }
 public static void LogCurrentConfigurations()
 {
     TextLogger.LogInfo("Service Fabric Autopilot Agent configurations:");
     TextLogger.LogInfo("BackgroundStateMachineRunIntervalSeconds = {0}", BackgroundStateMachineRunIntervalSeconds);
     TextLogger.LogInfo("DeploymentRetryIntervalSeconds = {0}", DeploymentRetryIntervalSeconds);
     TextLogger.LogInfo("DynamicTopologyUpdateMode = {0}", DynamicTopologyUpdateMode);
     TextLogger.LogInfo("BootstrapClusterManifestLocation = {0}", BootstrapClusterManifestLocation);
     TextLogger.LogInfo("DynamicPortRangeStart = {0}", DynamicPortRangeStart);
     TextLogger.LogInfo("DynamicPortRangeEnd = {0}", DynamicPortRangeEnd);
     TextLogger.LogInfo("PortFowardingRulesCount = {0}", PortFowardingRules.Count);
 }
        public static bool TryLoadFromFile(string encryptedPrivateKeyFile, string encryptedPasswordFile, out PrivateKeyCertificate privateKeyCertificate)
        {
            privateKeyCertificate = null;

            try
            {
                byte[] encryptedPrivateKeyBytes = File.ReadAllBytes(encryptedPrivateKeyFile);
                byte[] encryptedPasswordBytes   = File.ReadAllBytes(encryptedPasswordFile);

                byte[] decryptedPrivateKeyBlob;
                byte[] decryptedPasswordBlob;

                using (ApSecretProtection protect = new ApSecretProtection(ConfigurationManager.EnableSecretStorePersistentCaching))
                {
                    decryptedPrivateKeyBlob = protect.Decrypt(encryptedPrivateKeyBytes);
                    decryptedPasswordBlob   = protect.Decrypt(encryptedPasswordBytes);
                }

                string decryptedPassword;
                using (MemoryStream stream = new MemoryStream(decryptedPasswordBlob))
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        decryptedPassword = reader.ReadToEnd().Trim();
                    }

                X509Certificate2 certificate = new X509Certificate2(decryptedPrivateKeyBlob, decryptedPassword, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);

                if (!certificate.HasPrivateKey)
                {
                    TextLogger.LogError("Certificate from encrypted private key file {0} should present a private key.", encryptedPrivateKeyFile);

                    return(false);
                }

                if (!(certificate.PrivateKey is RSACryptoServiceProvider))
                {
                    TextLogger.LogError("Private key certificate from encrypted private key file {0} does not use a RSA based asymmetric algorithm implementation.", encryptedPrivateKeyFile);

                    return(false);
                }

                privateKeyCertificate = new PrivateKeyCertificate(certificate);

                return(true);
            }
            catch (Exception e)
            {
                TextLogger.LogError("Failed to load private key certificate from encrypted private key file {0} / encrypted password file {1} : {2}", encryptedPrivateKeyFile, encryptedPasswordFile, e);

                return(false);
            }
        }
        private bool ApplyPortForwardingRule(PortFowardingRule portForwardingRule)
        {
            if (string.Compare(portForwardingRule.MachineFunction, "*", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(portForwardingRule.MachineFunction, APRuntime.MachineFunction, StringComparison.OrdinalIgnoreCase) == 0)
            {
                TextLogger.LogInfo("Applying port forwarding rule {0} from port {1} to port {2}.", portForwardingRule.RuleName, portForwardingRule.ListenPort, portForwardingRule.ConnectPort);

                return(this.CreatePortProxy(IPAddress.Any.ToString(), portForwardingRule.ListenPort, this.LocalMachineIpAddress, portForwardingRule.ConnectPort));
            }
            else
            {
                TextLogger.LogInfo("Port forwarding rule {0} does not apply to machine function {1}.", portForwardingRule.RuleName, APRuntime.MachineFunction);

                return(true);
            }
        }
        public void SetNextState(ServiceFabricAutopilotAgentState nextState, bool toIgnoreStateTransitionWhenStopping = true)
        {
            lock (this.StatusLock)
            {
                if (toIgnoreStateTransitionWhenStopping && this.State < ServiceFabricAutopilotAgentState.Started)
                {
                    TextLogger.LogVerbose("Application is stopping. Ignore state transition request to {0}.", nextState);

                    return;
                }

                TextLogger.LogInfo("State transition: {0} -> {1}", this.State, nextState);

                this.State = nextState;
            }
        }
        /*
         * Test mode allows testing of ServiceFabricAutopilotAgent outside an Autopilot hosting environment.
         * In test mode, all dependencies on AP Runtime are replaced by test environment equivalences:
         *      Logging => text log
         *      Configuration => default test configurations
         */
        public void Start(bool useTestMode = false)
        {
            string agentApplicationDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);

            if (!useTestMode)
            {
                APRuntime.Initialize(StringConstants.ApplicationConfigurationFileName);
            }

            TextLogger.InitializeLogging(StringConstants.CustomLogIdName, useTestMode, StringConstants.TestModeLogFileNameTemplate, StringConstants.TestModeLogDirectoryName, StringConstants.TestModeTraceListenerName);

            TextLogger.LogInfo("Starting Service Fabric Autopilot Agent.");

            if (!ConfigurationManager.GetCurrentConfigurations(useTestMode))
            {
                this.Exit(false);

                return;
            }

            TextLogger.SetLogLevel(ConfigurationManager.LogLevel);

            ConfigurationManager.LogCurrentConfigurations();

            ServiceFabricLocalNodeManager localNodeManager;

            if (!ServiceFabricLocalNodeManager.Create(agentApplicationDirectory, useTestMode, out localNodeManager))
            {
                this.Exit(false);

                return;
            }

            this.localNodeManager = localNodeManager;

            this.agentStatus.InitializeAgentStatus();

            this.RegisterConsoleCancelEventNotifications();

            this.backgroundStateMachineTask = Task.Factory.StartNew(
                () => this.RunBackgroundStateMachine(this.backgroundStateMachineCancellationTokenSource.Token),
                this.backgroundStateMachineCancellationTokenSource.Token,
                TaskCreationOptions.LongRunning,
                TaskScheduler.Default);
        }
Example #12
0
        public static bool ReportMachineProperty(string machineName, MachinePropertyLevel propertyLevel, string property, string value)
        {
            if (useTestMode)
            {
                TextLogger.LogInfo("Test mode machine property report => machine : {0}, level : {1}, property : {2}, value : {3}.", machineName, propertyLevel, property, value);

                return(true);
            }
            else
            {
                try
                {
                    UpdateMachinePropertyRequest request = new UpdateMachinePropertyRequest
                    {
                        StatusFlag  = "always",
                        Environment = APRuntime.EnvironmentName,
                    };

                    request.PropertyList.Add(
                        new DMMachineProperty
                    {
                        MachineName = machineName,
                        Level       = propertyLevel,
                        Property    = property,
                        Value       = value
                    });

                    DMServerResponseCode responseCode = dmCommands.UpdateMachineProperty(request);
                    if (responseCode != DMServerResponseCode.Ok)
                    {
                        TextLogger.LogError("Failed to report {0} level machine property {1} against machine {2} with value {3}. Response code : {4}.", propertyLevel, property, machineName, value, responseCode);

                        return(false);
                    }

                    return(true);
                }
                catch (Exception e)
                {
                    TextLogger.LogError("Failed to report {0} level machine property {1} against machine {2} with value {3} : {4}.", propertyLevel, property, machineName, value, e);

                    return(false);
                }
            }
        }
        private bool ComputeNextStateFromManagingNetworkConfigurationState(out ServiceFabricAutopilotAgentState nextState)
        {
            nextState = ServiceFabricAutopilotAgentState.None;

            try
            {
                bool serviceFabricNodeConfigurationCompleted = Utilities.IsWindowsFabricNodeConfigurationCompleted(false);

                TextLogger.LogInfo("Service Fabric node configuration completed locally :  {0}.", serviceFabricNodeConfigurationCompleted);

                if (!serviceFabricNodeConfigurationCompleted)
                {
                    nextState = ServiceFabricAutopilotAgentState.InstallingProduct;
                }
                else
                {
                    ServiceController fabricHostService = this.localNodeManager.GetInstalledService(Constants.FabricHostServiceName);

                    if (fabricHostService == null)
                    {
                        // TODO: consider assert instead of an error message
                        TextLogger.LogError("Service Fabric node configuration has completed locally while Fabric host service does not exist.");

                        return(false);
                    }

                    if (fabricHostService.Status != ServiceControllerStatus.Running)
                    {
                        nextState = ServiceFabricAutopilotAgentState.StartingNode;
                    }
                    else
                    {
                        nextState = ServiceFabricAutopilotAgentState.NodeStarted;
                    }
                }

                return(true);
            }
            catch (Exception e)
            {
                TextLogger.LogError("Failed to compute next state from {0} state : {1}", ServiceFabricAutopilotAgentState.ManagingNetworkConfiguration, e);

                return(false);
            }
        }
        private async Task RunCertificateManager(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                bool succeeded = this.certificateManager.EnsureEnvironmentCertificatesPresence();

                if (succeeded)
                {
                    await Task.Delay(TimeSpan.FromSeconds(ConfigurationManager.CertificateManagerRunIntervalSeconds), cancellationToken);
                }
                else
                {
                    TextLogger.LogInfo("Certificate manager run failed. Retry after {0} seconds.", ConfigurationManager.CertificateManagerRetryIntervalSeconds);

                    await Task.Delay(TimeSpan.FromSeconds(ConfigurationManager.CertificateManagerRetryIntervalSeconds), cancellationToken);
                }
            }
        }
        private bool GetLocalMachineSku(out string localMachineSku)
        {
            localMachineSku = null;

            if (this.useTestMode)
            {
                localMachineSku = "TestModeMachineSku";

                return(true);
            }

            // When server pool is enabled, logical machine name would be used. Otherwise, physical machine name would be used.
            // Note that in a server pool disabled environment, logical machine names would be the same as physical machine names.
            string machineInfoFile  = Path.Combine(APRuntime.DataDirectory, StringConstants.MachineInfoFileName);
            string localMachineName = APRuntime.MachineName;

            try
            {
                using (CsvReader reader = new CsvReader(machineInfoFile))
                {
                    while (reader.Read())
                    {
                        if (string.Compare(reader["MachineName"], localMachineName, StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            localMachineSku = reader["SKU"];

                            TextLogger.LogInfo("Local machine {0} is using machine SKU {1}.", localMachineName, localMachineSku);

                            return(true);
                        }
                    }
                }

                TextLogger.LogError("Local machine {0} is not in machine info file {1}.", localMachineName, machineInfoFile);

                return(false);
            }
            catch (Exception e)
            {
                TextLogger.LogError("Failed to get machine SKU of local machine {0} from machine info file {1} : {2}", localMachineName, machineInfoFile, e);

                return(false);
            }
        }
        private void LogClusterTopology(ClusterManifestTypeInfrastructureWindowsAzureStaticTopology clusterTopology)
        {
            TextLogger.LogInfo("Cluster topology from static topology provider:");

            TextLogger.LogInfo("NodeName, NodeType, IsSeedNode, UpgradeDomain, FaultDomain, IPAddressOrFQDN");

            foreach (FabricNodeType node in clusterTopology.NodeList)
            {
                TextLogger.LogInfo(
                    "{0}, {1}, {2}, {3}, {4}, {5}",
                    node.NodeName,
                    node.NodeTypeRef,
                    node.IsSeedNode,
                    node.UpgradeDomain,
                    node.FaultDomain,
                    node.IPAddressOrFQDN
                    );
            }
        }
        private bool ReadBootstrapClusterManifestFile(out ClusterManifestType bootstrapClusterManifest)
        {
            bootstrapClusterManifest = null;

            string bootstrapClusterManifestFile = this.GetBootstrapClusterManifestFile();

            try
            {
                if (!File.Exists(bootstrapClusterManifestFile))
                {
                    TextLogger.LogError("Bootstrap cluster manifest does not exist in application directory as {0}.", bootstrapClusterManifestFile);

                    return(false);
                }

                /*
                 * As part of application directory, the bootstrap cluster manifest would only be updated via application upgrades where the agent host would be stopped prior to the update.
                 */
                using (FileStream fileStream = File.Open(bootstrapClusterManifestFile, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    // TODO: validation against schema
                    XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
                    xmlReaderSettings.ValidationType = ValidationType.None;
                    xmlReaderSettings.XmlResolver    = null;

                    using (XmlReader xmlReader = XmlReader.Create(fileStream, xmlReaderSettings))
                    {
                        XmlSerializer serializer = new XmlSerializer(typeof(ClusterManifestType));

                        bootstrapClusterManifest = (ClusterManifestType)serializer.Deserialize(xmlReader);
                    }
                }

                return(true);
            }
            catch (Exception e)
            {
                TextLogger.LogError("Failed to read bootstrap cluster manifest file {0} : {1}", bootstrapClusterManifestFile, e);

                return(false);
            }
        }
        private bool SetDynamicPortRange(int startPort, int endPort)
        {
            int portCount = endPort - startPort + 1;

            TextLogger.LogInfo("Setting dynamic port range. Start port : {0}. Port count : {1}.", startPort, portCount);

            if (Socket.OSSupportsIPv4)
            {
                if (!SetDynamicPortRange(startPort, portCount, "ipv4", "tcp"))
                {
                    TextLogger.LogError("Failed to set dynamic port range for ipv4:tcp. Start port : {0}. Port count : {1}.", startPort, portCount);

                    return(false);
                }

                if (!SetDynamicPortRange(startPort, portCount, "ipv4", "udp"))
                {
                    TextLogger.LogError("Failed to set dynamic port range for ipv4:udp. Start port : {0}. Port count : {1}.", startPort, portCount);

                    return(false);
                }
            }

            if (Socket.OSSupportsIPv6)
            {
                if (!SetDynamicPortRange(startPort, portCount, "ipv6", "tcp"))
                {
                    TextLogger.LogError("Failed to set dynamic port range for ipv6:tcp. Start port : {0}. Port count : {1}.", startPort, portCount);

                    return(false);
                }

                if (!SetDynamicPortRange(startPort, portCount, "ipv6", "udp"))
                {
                    TextLogger.LogError("Failed to set dynamic port range for ipv6:udp. Start port : {0}. Port count : {1}.", startPort, portCount);

                    return(false);
                }
            }

            return(true);
        }
        private bool InvokeFabricDeployer(ClusterTopology topologyToDeploy, DeploymentType deploymentType, bool toUseCurrentClusterManifestForDeployment)
        {
            TextLogger.LogInfo("Invoking FabricDeployer with deployment type = {0} and toUseCurrentClusterManifestForDeployment = {1}.", deploymentType, toUseCurrentClusterManifestForDeployment);

            string clusterManifestFileToDeploy;

            if (toUseCurrentClusterManifestForDeployment)
            {
                string currentClusterManifestFile = this.GetCurrentClusterManifestFile(topologyToDeploy.CurrentNodeName);

                if (File.Exists(currentClusterManifestFile))
                {
                    TextLogger.LogInfo("Using current cluster manifest file {0} for deployment.", currentClusterManifestFile);

                    clusterManifestFileToDeploy = currentClusterManifestFile;
                }
                else
                {
                    TextLogger.LogError("Current cluster manifest file does not exist as {0} when toUseCurrentClusterManifestForDeployment = true.", currentClusterManifestFile);

                    return(false);
                }
            }
            else
            {
                clusterManifestFileToDeploy = this.GetBootstrapClusterManifestFile();
            }

            if (!this.FabricDeploy(deploymentType, clusterManifestFileToDeploy, this.DataRootDirectory, topologyToDeploy.ServiceFabricNodes, true))
            {
                TextLogger.LogError(
                    "Failed to invoke FabricDeployer with deployment type = {0}, cluster manifest file = {1}, data root directory = {2}.",
                    deploymentType,
                    clusterManifestFileToDeploy,
                    this.DataRootDirectory);

                return(false);
            }

            return(true);
        }
        private void ConsoleCancelEventHanlder(object sender, ConsoleCancelEventArgs args)
        {
            TextLogger.LogInfo("Cancel event (Ctrl + C / Ctrl + Break) received. Stopping Service Fabric Autopilot Agent.");

            if (this.agentStatus.GetCurrentState() < ServiceFabricAutopilotAgentState.Started)
            {
                return;
            }

            this.agentStatus.SetNextState(ServiceFabricAutopilotAgentState.Stopping, false);

            this.backgroundStateMachineCancellationTokenSource.Cancel();

            this.localNodeManager.StopLocalNode();

            this.agentStatus.SetNextState(ServiceFabricAutopilotAgentState.Stopped, false);

            TextLogger.LogInfo("Service Fabric Autopilot Agent has stopped. Exiting the process.");

            this.Exit(true);
        }
        private string GetBootstrapClusterManifestFile()
        {
            string bootstrapClusterManifestFile;

            if (ConfigurationManager.BootstrapClusterManifestLocation == BootstrapClusterManifestLocationType.Default || this.useTestMode)
            {
                bootstrapClusterManifestFile = Path.Combine(this.agentApplicationDirectory, StringConstants.DefaultBootstrapClusterManifestFileName);
            }
            else if (ConfigurationManager.BootstrapClusterManifestLocation == BootstrapClusterManifestLocationType.EnvironmentDefault)
            {
                bootstrapClusterManifestFile = Path.Combine(this.agentApplicationDirectory, StringConstants.EnvironmentDefaultConfigDirectoryName, APRuntime.ClusterName, APRuntime.EnvironmentName, StringConstants.DefaultBootstrapClusterManifestFileName);
            }
            else
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Bootstrap cluster manifest file is not available based on current BootstrapClusterManifestLocation {0}.", ConfigurationManager.BootstrapClusterManifestLocation));
            }

            TextLogger.LogInfo("Based on bootstrapClusterManifestLocation {0} and useTestMode {1}, bootstrapClusterManifestFile = {2}", ConfigurationManager.BootstrapClusterManifestLocation, this.useTestMode, bootstrapClusterManifestFile);

            return(bootstrapClusterManifestFile);
        }
Example #22
0
        internal void LogTopology()
        {
            if (this.ServiceFabricNodes.Count == 0)
            {
                TextLogger.LogInfo("Static topology defined in cluster manifest + dynamic topology update disabled.");
            }
            else
            {
                TextLogger.LogInfo("Static topology defined in cluster manifest + dynamic topology update with {0} nodes", this.ServiceFabricNodes.Count);
                TextLogger.LogInfo("NodeName, NodeType, UpgradeDomain, FaultDomain, IPAddressOrFQDN");

                foreach (InfrastructureNodeType node in this.ServiceFabricNodes)
                {
                    TextLogger.LogInfo(
                        "{0}, {1}, {2}, {3}, {4}",
                        node.NodeName,
                        node.NodeTypeRef,
                        node.UpgradeDomain,
                        node.FaultDomain,
                        node.IPAddressOrFQDN
                        );
                }
            }
        }
        public void Start()
        {
            string applicationDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);

            this.certificateManager = new EnvironmentCertificateManager(applicationDirectory);

            APRuntime.Initialize(StringConstants.ApplicationConfigurationFileName);

            TextLogger.InitializeLogging(StringConstants.CustomLogIdName);

            TextLogger.LogInfo("Starting Service Fabric Environment Sync.");

            if (!ConfigurationManager.GetCurrentConfigurations())
            {
                this.Exit(false);

                return;
            }

            TextLogger.SetLogLevel(ConfigurationManager.LogLevel);

            if (!this.certificateManager.GetEnvironmentCertificatesFromCurrentConfigurations())
            {
                this.Exit(false);

                return;
            }

            this.RegisterConsoleCancelEventNotifications();

            this.certificateManagerTask = Task.Factory.StartNew(
                () => this.RunCertificateManager(this.certificateManagerCancellationTokenSource.Token),
                this.certificateManagerCancellationTokenSource.Token,
                TaskCreationOptions.LongRunning,
                TaskScheduler.Default);
        }
        private void RunStateMachine()
        {
            bool toRunStateMachineCore = false;

            lock (this.agentStatus.StatusLock)
            {
                if (this.agentStatus.State < ServiceFabricAutopilotAgentState.Started)
                {
                    return;
                }

                TextLogger.LogVerbose("RunStateMachine : {0}.", this.agentStatus);

                if (this.agentStatus.LastFailedDeploymentAction != DateTime.MinValue && (DateTime.UtcNow - this.agentStatus.LastFailedDeploymentAction).TotalSeconds < ConfigurationManager.DeploymentRetryIntervalSeconds)
                {
                    return;
                }

                // Note that when a deployer operation (node configuration creation or update) fails, it would be retried based on the same cluster manifest (bootstrap or current).
                // I.e a retry would happen even when a previous operation failed with the same cluster manifest.
                if ((this.agentStatus.State > ServiceFabricAutopilotAgentState.Started && agentStatus.State < ServiceFabricAutopilotAgentState.DiscoveringTopology) ||
                    (this.agentStatus.State == ServiceFabricAutopilotAgentState.DiscoveringTopology && ConfigurationManager.BootstrapClusterManifestLocation != BootstrapClusterManifestLocationType.NotReady) ||
                    (this.agentStatus.State > ServiceFabricAutopilotAgentState.DiscoveringTopology && agentStatus.State < ServiceFabricAutopilotAgentState.NodeStarted) ||
                    (this.agentStatus.State == ServiceFabricAutopilotAgentState.NodeStarted && !this.agentStatus.ExpectedTopologyRunning))
                {
                    TextLogger.LogInfo(
                        "RunStateMachine: current agent state necessitates RunStateMachineCore. Agent status = {0}, ExpectedTopologyRunning = {1}, LastFailedDeploymentAction = {2}, BootstrapClusterManifestLocation = {3}.",
                        this.agentStatus.State,
                        this.agentStatus.ExpectedTopologyRunning,
                        this.agentStatus.LastFailedDeploymentAction,
                        ConfigurationManager.BootstrapClusterManifestLocation);

                    toRunStateMachineCore = true;
                }
            }

            if (toRunStateMachineCore)
            {
                bool coreStateMachineRunResult;
                ServiceFabricAutopilotAgentState currentState;

                do
                {
                    currentState = this.agentStatus.GetCurrentState();

                    if (currentState < ServiceFabricAutopilotAgentState.Started)
                    {
                        return;
                    }

                    coreStateMachineRunResult = this.RunStateMachineCore(currentState);

                    TextLogger.LogInfo("RunStateMachine: RunStateMachineCore completed with result {0}. State prior to the run : {1}.", coreStateMachineRunResult, currentState);

                    if (!coreStateMachineRunResult)
                    {
                        lock (agentStatus.StatusLock)
                        {
                            this.agentStatus.LastFailedDeploymentAction = DateTime.UtcNow;
                        }
                    }
                    else
                    {
                        lock (agentStatus.StatusLock)
                        {
                            this.agentStatus.LastFailedDeploymentAction = DateTime.MinValue;
                        }
                    }

                    currentState = this.agentStatus.GetCurrentState();
                }while (currentState > ServiceFabricAutopilotAgentState.Started && currentState < ServiceFabricAutopilotAgentState.NodeStarted && coreStateMachineRunResult);
            }
        }
        /*
         * TODO: currently AP safe config deployment is not supported to update configurations.
         * The only way to update the configurations is via an application upgrade.
         * This is OK since none of the configurations are critical.
         * To update all things critical in Service Fabric Autopilot Agent, an AP application upgrade is needed.
         */
        public static bool GetCurrentConfigurations(bool useTestMode = false)
        {
            if (useTestMode)
            {
                GetTestConfigurations();
            }
            else
            {
                IConfiguration configuration = APConfiguration.GetConfiguration();

                BackgroundStateMachineRunIntervalSeconds = configuration.GetInt32Value(StringConstants.AgentConfigurationSectionName, "BackgroundStateMachineRunIntervalSeconds", 10);

                DeploymentRetryIntervalSeconds = configuration.GetInt32Value(StringConstants.AgentConfigurationSectionName, "DeploymentRetryIntervalSeconds", 120);

                string bootstrapClusterManifestLocationString = configuration.GetStringValue(StringConstants.AgentConfigurationSectionName, "BootstrapClusterManifestLocation", string.Empty);

                BootstrapClusterManifestLocationType bootstrapClusterManifestLocation;
                if (!Enum.TryParse <BootstrapClusterManifestLocationType>(bootstrapClusterManifestLocationString, out bootstrapClusterManifestLocation))
                {
                    TextLogger.LogError("Bootstrap cluster manifest location is invalid or not specified : {0}.", bootstrapClusterManifestLocationString);

                    return(false);
                }

                BootstrapClusterManifestLocation = bootstrapClusterManifestLocation;

                DynamicPortRangeStart = configuration.GetInt32Value(StringConstants.AgentConfigurationSectionName, "DynamicPortRangeStart", -1);

                DynamicPortRangeEnd = configuration.GetInt32Value(StringConstants.AgentConfigurationSectionName, "DynamicPortRangeEnd", -1);

                // please refer to http://support.microsoft.com/kb/929851 for valid dynamic port range in Windows.
                if (DynamicPortRangeStart < 1025 || DynamicPortRangeEnd > 65535 || DynamicPortRangeEnd - DynamicPortRangeStart + 1 < 255)
                {
                    TextLogger.LogError("Dynamic port range is invalid or not specified : [{0}, {1}].", DynamicPortRangeStart, DynamicPortRangeEnd);

                    return(false);
                }

                DynamicTopologyUpdateMode = (DynamicTopologyUpdateMode)configuration.GetInt32Value(StringConstants.AgentConfigurationSectionName, "DynamicTopologyUpdateMode", 0);

                string logLevelString = configuration.GetStringValue(StringConstants.AgentConfigurationSectionName, "LogLevel", "Info");

                LogLevel logLevel;
                if (!Enum.TryParse <LogLevel>(logLevelString, out logLevel))
                {
                    TextLogger.LogError("Log level is invalid : {0}.", logLevelString);

                    return(false);
                }

                LogLevel = logLevel;

                PortFowardingRules = new List <PortFowardingRule>();
                if (configuration.SectionExists(StringConstants.PortForwardingConfigurationSectionName))
                {
                    string[] portFowardingRuleNames = configuration.GetSectionKeys(StringConstants.PortForwardingConfigurationSectionName);

                    if (portFowardingRuleNames != null)
                    {
                        foreach (string portFowardingRuleName in portFowardingRuleNames)
                        {
                            string[] portFowardingRuleComponents = configuration.GetStringValueAndSplit(StringConstants.PortForwardingConfigurationSectionName, portFowardingRuleName, ",");

                            if (portFowardingRuleComponents == null || portFowardingRuleComponents.Length != 3)
                            {
                                TextLogger.LogError("Port forwarding rule {0} is invalid. Valid rule format : RuleName=ListenPort,ConnectPort,MachineFunction", portFowardingRuleName);

                                return(false);
                            }

                            int listenPort;
                            if (!int.TryParse(portFowardingRuleComponents[0], out listenPort) || listenPort <= 0 || listenPort > 65535)
                            {
                                TextLogger.LogError("Port forwarding rule {0} is invalid with invalid listen port {1}. Valid rule format : RuleName=ListenPort,ConnectPort,MachineFunction", portFowardingRuleName, portFowardingRuleComponents[0]);

                                return(false);
                            }

                            int connectPort;
                            if (!int.TryParse(portFowardingRuleComponents[1], out connectPort) || connectPort <= 0 || connectPort > 65535)
                            {
                                TextLogger.LogError("Port forwarding rule {0} is invalid with invalid connect port {1}. Valid rule format : RuleName=ListenPort,ConnectPort,MachineFunction", portFowardingRuleName, portFowardingRuleComponents[1]);

                                return(false);
                            }

                            PortFowardingRules.Add(new PortFowardingRule {
                                RuleName = portFowardingRuleName, ListenPort = listenPort, ConnectPort = connectPort, MachineFunction = portFowardingRuleComponents[2]
                            });
                        }
                    }
                }
            }

            return(true);
        }
        private void Exit(bool succeeded)
        {
            TextLogger.Flush();

            this.exitCallback(succeeded);
        }
        public bool DiscoverTopology(out ClusterTopology expectedTopology)
        {
            expectedTopology = null;

            try
            {
                TextLogger.LogInfo("Discovering cluster topology from bootstrap cluster manifest file.");

                if (ConfigurationManager.BootstrapClusterManifestLocation == BootstrapClusterManifestLocationType.NotReady)
                {
                    TextLogger.LogWarning("Bootstrap cluster manifest is not ready. Cluster topology would not be discovered until bootstrap cluster manifest is ready.");

                    return(false);
                }

                ClusterManifestType bootstrapClusterManifest;
                if (!this.ReadBootstrapClusterManifestFile(out bootstrapClusterManifest))
                {
                    return(false);
                }

                TopologyProviderType topologyProviderType = TopologyProvider.GetTopologyProviderTypeFromClusterManifest(bootstrapClusterManifest);

                TextLogger.LogInfo("Topology provider type : {0}.", topologyProviderType);

                if (topologyProviderType != TopologyProviderType.StaticTopologyProvider)
                {
                    TextLogger.LogInfo("Topology provider type {0} is not supported to provide static topology.", topologyProviderType);

                    return(false);
                }
                else
                {
                    /*
                     * Static topology provider defines the authoritative cluster topology in the cluster manifest.
                     * If the local machine (physical or virtual) is not part of the cluster topology specified in static topology provider section, a Service Fabric node will not be started locally.
                     * During scale-up, bootstrap cluster manifest has to be upgraded to allow new Service Fabric nodes to be started on the new machines and join the ring.
                     * A scale-up is normally a two-phase process with allocation of new machines (and their IPs) and generation of a new bootstrap cluster manifest with updated topology.
                     */
                    ClusterManifestTypeInfrastructureWindowsAzureStaticTopology staticTopologyProviderElement = TopologyProvider.GetStaticTopologyProviderElementFromClusterManifest(bootstrapClusterManifest);

                    if (staticTopologyProviderElement.NodeList == null || staticTopologyProviderElement.NodeList.Length == 0)
                    {
                        TextLogger.LogError("Static topology provider section of bootstrap cluster manifest does not specify topology of the Service Fabric cluster.");

                        return(false);
                    }

                    LogClusterTopology(staticTopologyProviderElement);

                    ClusterTopology staticTopologyProviderClusterTopology = ClusterTopology.GetClusterTopology();

                    staticTopologyProviderClusterTopology.LogTopology();

                    expectedTopology = staticTopologyProviderClusterTopology;

                    TextLogger.LogInfo("Successfully discovered cluster topology.");

                    return(true);
                }
            }
            catch (Exception e)
            {
                TextLogger.LogError("Failed to discover cluster topology : {0}", e);

                return(false);
            }
        }
        public bool StartLocalNode()
        {
            try
            {
                TextLogger.LogInfo("Starting local Service Fabric node.");

                // TODO: After current xcopyable issue is fixed, update the pattern here.

                // TODO: [General] disposal of ServiceController
                ServiceController fabricHostService = this.GetInstalledService(Constants.FabricHostServiceName);

                if (fabricHostService != null)
                {
                    if (fabricHostService.Status == ServiceControllerStatus.StartPending)
                    {
                        TextLogger.LogWarning("Fabric host service is starting.");

                        return(false);
                    }
                    else if (fabricHostService.Status == ServiceControllerStatus.Running)
                    {
                        TextLogger.LogInfo("Successfully started local Service Fabric node.");

                        return(true);
                    }
                    else if (!this.StartFabricHostService())
                    {
                        TextLogger.LogError("Failed to start local Service Fabric node ");

                        return(false);
                    }
                }
                else
                {
                    ServiceController fabricInstallerService = this.GetInstalledService(Constants.FabricInstallerServiceName);

                    // Install and configure Fabric installer service if not installed.
                    if (fabricInstallerService == null)
                    {
                        TextLogger.LogInfo("Installing and configuring Fabric installer service.");

                        if (!this.InstallAndConfigureFabricInstallerService(this.agentApplicationDirectory))
                        {
                            TextLogger.LogError("Failed to install and configure Fabric installer service");

                            return(false);
                        }

                        fabricInstallerService = this.GetInstalledService(Constants.FabricInstallerServiceName);
                    }

                    // If Fabric installer service is not in stopped state, stop it.
                    if (fabricInstallerService.Status != ServiceControllerStatus.Stopped)
                    {
                        TextLogger.LogInfo("Fabric installer service is in {0} state. Stopping it.", fabricInstallerService.Status);

                        if (!this.StopFabricInstallerService())
                        {
                            TextLogger.LogError("Failed to stop Fabric installer service.");

                            return(false);
                        }
                    }

                    // Start Fabric installer service to start Fabric host service.
                    TextLogger.LogInfo("Starting Fabric installer service to start Fabric host service.");
                    if (!this.StartFabricInstallerService())
                    {
                        TextLogger.LogError("Failed to start Fabric installer service.");

                        return(false);
                    }
                }

                TextLogger.LogInfo("Successfully started local Service Fabric node.");

                return(true);
            }
            catch (Exception e)
            {
                TextLogger.LogError("Failed to start local Service Fabric node : {0}", e);

                return(false);
            }
        }
        private bool RunStateMachineCore(ServiceFabricAutopilotAgentState currentState)
        {
            TextLogger.LogInfo("Starting RunStateMachineCore. Current state : {0}.", currentState);

            switch (currentState)
            {
            case ServiceFabricAutopilotAgentState.ManagingNetworkConfiguration:
            {
                if (!this.localNodeManager.ManageNetworkConfiguration())
                {
                    return(false);
                }

                ServiceFabricAutopilotAgentState nextState;
                if (!this.ComputeNextStateFromManagingNetworkConfigurationState(out nextState))
                {
                    return(false);
                }

                this.agentStatus.SetNextState(nextState);

                break;
            }

            case ServiceFabricAutopilotAgentState.InstallingProduct:
            {
                if (!this.localNodeManager.InstallProduct())
                {
                    return(false);
                }

                // For now, we assume the topology is static.
                // TODO: when adding dynamic topology support, change state machine transition here.
                this.agentStatus.SetNextState(ServiceFabricAutopilotAgentState.DiscoveringTopology);

                break;
            }

            case ServiceFabricAutopilotAgentState.DiscoveringTopology:
            {
                ClusterTopology expectedTopology;
                if (!this.localNodeManager.DiscoverTopology(out expectedTopology))
                {
                    return(false);
                }

                lock (this.agentStatus.StatusLock)
                {
                    this.agentStatus.ExpectedTopology = expectedTopology;

                    this.agentStatus.ExpectedTopologyRunning = false;
                }

                this.agentStatus.SetNextState(ServiceFabricAutopilotAgentState.ConfiguringNode);

                break;
            }

            case ServiceFabricAutopilotAgentState.ConfiguringNode:
            {
                ClusterTopology topologyToDeploy = this.agentStatus.GetTopologyToDeploy();

                if (!this.localNodeManager.ConfigureLocalNode(topologyToDeploy))
                {
                    return(false);
                }

                lock (this.agentStatus.StatusLock)
                {
                    this.agentStatus.DeployedTopology = topologyToDeploy;

                    this.agentStatus.ExpectedTopologyRunning = !this.agentStatus.DeployedTopology.IsClusterTopologyChanged(this.agentStatus.ExpectedTopology);
                }

                this.agentStatus.SetNextState(ServiceFabricAutopilotAgentState.StartingNode);

                break;
            }

            case ServiceFabricAutopilotAgentState.StartingNode:
            {
                if (!this.localNodeManager.StartLocalNode())
                {
                    return(false);
                }

                this.agentStatus.SetNextState(ServiceFabricAutopilotAgentState.NodeStarted);

                break;
            }

            case ServiceFabricAutopilotAgentState.NodeStarted:
            {
                // TODO: this only applies when DynamicTopologyUpdateMode == None or OnNodeConfigurationOnly.
                // When we want to update topology whenever topology changes, this needs to change.
                lock (this.agentStatus.StatusLock)
                {
                    this.agentStatus.ExpectedTopologyRunning = true;
                    this.agentStatus.ExpectedTopology        = ClusterTopology.GetClusterTopology();
                    this.agentStatus.DeployedTopology        = this.agentStatus.ExpectedTopology;
                }

                break;
            }
            }

            return(true);
        }
        private bool GetAndCreateDataRootDirectory(out string dataRootDirectory)
        {
            dataRootDirectory = null;

            try
            {
                // On AP machines, data root would depend on machine SKU (specifically the SKU's disk layout). For test mode testing, use C:\SFRoot.
                if (this.useTestMode)
                {
                    dataRootDirectory = Path.Combine(Path.GetPathRoot("C:\\"), StringConstants.ServiceFabricRootDirectoryName);

                    TextLogger.LogInfo("Using {0} as data root directory in test mode.", dataRootDirectory);
                }
                else
                {
                    string diskLayoutConfigurationFile = Path.Combine(this.agentApplicationDirectory, StringConstants.DiskLayoutConfigurationFileName);

                    TextLogger.LogInfo("Data root directory would be determined based on local machine SKU {0} and disk layout configuration file {1}.", this.LocalMachineSku, diskLayoutConfigurationFile);

                    string dataRootHint = null;
                    using (CsvReader reader = new CsvReader(diskLayoutConfigurationFile))
                    {
                        while (reader.Read())
                        {
                            if (Regex.IsMatch(this.LocalMachineSku, reader["SKU"], RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
                            {
                                dataRootHint = Path.GetFullPath(Environment.ExpandEnvironmentVariables(reader["DataRootHint"]));

                                TextLogger.LogInfo(
                                    "Local machine SKU {0} matched disk layout SKU rule '{1}' -> '{2}' which expands to data root '{3}'",
                                    this.LocalMachineSku,
                                    reader["SKU"],
                                    reader["DataRootHint"],
                                    dataRootHint);

                                break;
                            }
                        }
                    }

                    if (string.IsNullOrEmpty(dataRootHint))
                    {
                        TextLogger.LogError("Disk layout configuration file {0} does not contain a valid entry for local machine SKU {1}.", diskLayoutConfigurationFile, this.LocalMachineSku);

                        return(false);
                    }

                    dataRootDirectory = Path.Combine(Path.GetPathRoot(dataRootHint), StringConstants.ServiceFabricRootDirectoryName);

                    TextLogger.LogInfo("Using {0} as data root directory based on local machine SKU {1} and disk layout configuration file {2}.", dataRootDirectory, this.LocalMachineSku, diskLayoutConfigurationFile);
                }

                TextLogger.LogInfo("Creating data root directory {0}.", dataRootDirectory);

                Directory.CreateDirectory(dataRootDirectory);

                return(true);
            }
            catch (Exception e)
            {
                TextLogger.LogError("Failed to get and create data root directory : {0}", e);

                return(false);
            }
        }