/// <summary> /// Pings one or more hostnames or IP addresses in parallel to identify one that /// appears to be online and reachable via the network (because it answers a ping). /// </summary> /// <param name="hosts">The hostname or IP addresses to be tested.</param> /// <param name="failureMode"> /// Specifies what should happen when there are no reachable hosts. /// This defaults to <see cref="ReachableHostMode.ReturnFirst"/>. /// </param> /// <returns>A <see cref="ReachableHost"/> instance describing the host or <c>null</c>.</returns> /// <exception cref="NetworkException"> /// Thrown if no hosts are reachable and <paramref name="failureMode"/> is /// passed as <see cref="ReachableHostMode.Throw"/>. /// </exception> public static ReachableHost GetReachableHost(IEnumerable <string> hosts, ReachableHostMode failureMode = ReachableHostMode.ReturnFirst) { Covenant.Requires <ArgumentNullException>(hosts != null, nameof(hosts)); Covenant.Requires <ArgumentNullException>(hosts.Count() > 0, nameof(hosts)); var reachableHosts = GetReachableHosts(hosts); // We want to favor reachable hosts that appear earlier in the // hosts list passed over hosts that appear later. if (!reachableHosts.IsEmpty()) { foreach (var host in hosts) { foreach (var reachableHost in reachableHosts) { if (host == reachableHost.Host) { return(reachableHost); } } } } // None of the hosts responded so the result is determined by the // failure mode. switch (failureMode) { case ReachableHostMode.ReturnFirst: var firstHost = hosts.First(); return(new ReachableHost(firstHost, null, TimeSpan.Zero, unreachable: true)); case ReachableHostMode.ReturnNull: return(null); case ReachableHostMode.Throw: throw new NetworkException("None of the hosts responded."); default: throw new NotImplementedException($"Unexpected failure [mode={failureMode}]."); } }
/// <summary> /// Returns a master node that is reachable via the network because it answers a ping. /// </summary> /// <param name="failureMode">Specifies what should happen when there are no reachable masters.</param> /// <returns>The reachable master node or <c>null</c>.</returns> /// <exception cref="KubeException"> /// Thrown if no masters are reachable and <paramref name="failureMode"/> /// is passed as <see cref="ReachableHostMode.Throw"/>. /// </exception> public SshProxy <NodeDefinition> GetReachableMaster(ReachableHostMode failureMode = ReachableHostMode.ReturnFirst) { var masterAddresses = Nodes .Where(n => n.Metadata.IsMaster) .Select(n => n.PrivateAddress.ToString()) .ToList(); var reachableHost = NetHelper.GetReachableHost(masterAddresses, failureMode); if (reachableHost == null) { return(null); } // Return the node that is assigned the reachable address. return(Nodes.Where(n => n.PrivateAddress.ToString() == reachableHost.Host).First()); }
/// <summary> /// Selects a cluster node from the set of nodes that match a predicate that is /// reachable via the network because it answers a ping. /// </summary> /// <param name="predicate">Predicate used to select the candidate nodes.</param> /// <param name="failureMode">Specifies what should happen when there are no reachable nodes.</param> /// <returns>The reachable node or <c>null</c>.</returns> /// <exception cref="KubeException"> /// Thrown if no nodes matching the predicate are reachable and <paramref name="failureMode"/> /// is passed as <see cref="ReachableHostMode.Throw"/>. /// </exception> public SshProxy <NodeDefinition> GetReachableNode(Func <SshProxy <NodeDefinition>, bool> predicate, ReachableHostMode failureMode = ReachableHostMode.ReturnFirst) { var nodeAddresses = Nodes .Where(predicate) .Select(n => n.PrivateAddress.ToString()) .ToList(); var reachableHost = NetHelper.GetReachableHost(nodeAddresses, failureMode); if (reachableHost == null) { return(null); } // Return the node that is assigned the reachable address. return(Nodes.Where(n => n.PrivateAddress.ToString() == reachableHost.Host).First()); }