diff --git a/Consul/Agent.cs b/Consul/Agent.cs index 1350f84..d575bed 100644 --- a/Consul/Agent.cs +++ b/Consul/Agent.cs @@ -26,598 +26,601 @@ namespace Consul { - /// - /// The status of a TTL check - /// - public class TTLStatus : IEquatable - { - private static readonly TTLStatus passingStatus = new TTLStatus() { Status = "passing", LegacyStatus = "pass" }; - private static readonly TTLStatus warningStatus = new TTLStatus() { Status = "warning", LegacyStatus = "warn" }; - private static readonly TTLStatus criticalStatus = new TTLStatus() { Status = "critical", LegacyStatus = "fail" }; - - public string Status { get; private set; } - internal string LegacyStatus { get; private set; } - - public static TTLStatus Pass - { - get { return passingStatus; } - } - - public static TTLStatus Warn - { - get { return warningStatus; } - } - - public static TTLStatus Critical - { - get { return criticalStatus; } - } - - [Obsolete("Use TTLStatus.Critical instead. This status will be an error in 0.7.0+", true)] - public static TTLStatus Fail - { - get { return criticalStatus; } - } - - public bool Equals(TTLStatus other) - { - return other != null && ReferenceEquals(this, other); - } - - public override bool Equals(object other) - { - // other could be a reference type, the is operator will return false if null - return other is TTLStatus && Equals(other as TTLStatus); - } - - public override int GetHashCode() - { - return Status.GetHashCode(); - } - } - - public class TTLStatusConverter : JsonConverter - { - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - serializer.Serialize(writer, ((TTLStatus)value).Status); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, - JsonSerializer serializer) - { - var status = (string)serializer.Deserialize(reader, typeof(string)); - switch (status) - { - case "pass": - return TTLStatus.Pass; - case "passing": - return TTLStatus.Pass; - case "warn": - return TTLStatus.Warn; - case "warning": - return TTLStatus.Warn; - case "fail": - return TTLStatus.Critical; - case "critical": - return TTLStatus.Critical; - default: - throw new ArgumentException("Invalid TTL status value during deserialization"); - } - } - - public override bool CanConvert(Type objectType) - { - return objectType == typeof(TTLStatus); - } - } - - /// - /// AgentCheck represents a check known to the agent - /// - public class AgentCheck - { - public string Node { get; set; } - public string CheckID { get; set; } - public string Name { get; set; } - - [JsonConverter(typeof(HealthStatusConverter))] - public HealthStatus Status { get; set; } - - public string Notes { get; set; } - public string Output { get; set; } - public string ServiceID { get; set; } - public string ServiceName { get; set; } - } - - /// - /// AgentService represents a service known to the agent - /// - public class AgentService - { - public string ID { get; set; } - public string Service { get; set; } - public string[] Tags { get; set; } - public int Port { get; set; } - public string Address { get; set; } - public bool EnableTagOverride { get; set; } - public IDictionary Meta { get; set; } - } - - /// - /// AgentMember represents a cluster member known to the agent - /// - public class AgentMember - { - public string Name { get; set; } - public string Addr { get; set; } - public ushort Port { get; set; } - public Dictionary Tags { get; set; } - public int Status { get; set; } - public byte ProtocolMin { get; set; } - public byte ProtocolMax { get; set; } - public byte ProtocolCur { get; set; } - public byte DelegateMin { get; set; } - public byte DelegateMax { get; set; } - public byte DelegateCur { get; set; } - - public AgentMember() - { - Tags = new Dictionary(); - } - } - - /// - /// AgentServiceRegistration is used to register a new service - /// - public class AgentServiceRegistration - { - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string ID { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string Name { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string[] Tags { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int Port { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string Address { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public bool EnableTagOverride { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public AgentServiceCheck Check { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public AgentServiceCheck[] Checks { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public IDictionary Meta { get; set; } - } - - /// - /// AgentCheckRegistration is used to register a new check - /// - public class AgentCheckRegistration : AgentServiceCheck - { - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string ID { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string Name { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string Notes { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string ServiceID { get; set; } - } - - /// - /// AgentServiceCheck is used to create an associated check for a service - /// - public class AgentServiceCheck - { - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string Script { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string DockerContainerID { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string Shell { get; set; } // Only supported for Docker. - - [JsonConverter(typeof(DurationTimespanConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TimeSpan? Interval { get; set; } - - [JsonConverter(typeof(DurationTimespanConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TimeSpan? Timeout { get; set; } - - [JsonConverter(typeof(DurationTimespanConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TimeSpan? TTL { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string HTTP { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string TCP { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - [JsonConverter(typeof(HealthStatusConverter))] - public HealthStatus Status { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public bool TLSSkipVerify { get; set; } - - /// - /// In Consul 0.7 and later, checks that are associated with a service - /// may also contain this optional DeregisterCriticalServiceAfter field, - /// which is a timeout in the same Go time format as Interval and TTL. If - /// a check is in the critical state for more than this configured value, - /// then its associated service (and all of its associated checks) will - /// automatically be deregistered. - /// - [JsonConverter(typeof(DurationTimespanConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TimeSpan? DeregisterCriticalServiceAfter { get; set; } - } - - public enum LogLevel - { - Info, - Trace, - Debug, - Warn, - Err - } - - /// - /// Agent can be used to query the Agent endpoints - /// - public class Agent : IAgentEndpoint - { - private class CheckUpdate - { - public string Status { get; set; } - public string Output { get; set; } - } - private readonly ConsulClient _client; - private string _nodeName; - private readonly AsyncLock _nodeNameLock; - - internal Agent(ConsulClient c) - { - _client = c; - _nodeNameLock = new AsyncLock(); - } - - /// - /// Self is used to query the agent we are speaking to for information about itself - /// - /// A somewhat dynamic object representing the various data elements in Self - public Task>>> Self(CancellationToken ct = default(CancellationToken)) - { - return _client.Get>>("/v1/agent/self").Execute(ct); - } - - /// - /// NodeName is used to get the node name of the agent - /// - [Obsolete("This property will be removed in 0.8.0. Replace uses of it with a call to 'await GetNodeName()'")] - public string NodeName - { - get - { - return GetNodeName().ConfigureAwait(false).GetAwaiter().GetResult(); - } - } - - /// - /// GetNodeName is used to get the node name of the agent. The value is cached per instance of ConsulClient after the first use. - /// - public async Task GetNodeName(CancellationToken ct = default(CancellationToken)) - { - if (_nodeName == null) - { - using (await _nodeNameLock.LockAsync().ConfigureAwait(false)) - { - if (_nodeName == null) - { - _nodeName = (await Self(ct)).Response["Config"]["NodeName"]; - } - } - } - - return _nodeName; - } - - /// - /// Checks returns the locally registered checks - /// - /// A map of the registered check names and check data - public Task>> Checks(CancellationToken ct = default(CancellationToken)) - { - return _client.Get>("/v1/agent/checks").Execute(ct); - } - - /// - /// Services returns the locally registered services - /// - /// A map of the registered services and service data - public Task>> Services(CancellationToken ct = default(CancellationToken)) - { - return _client.Get>("/v1/agent/services").Execute(ct); - } - - /// - /// Members returns the known gossip members. The WAN flag can be used to query a server for WAN members. - /// - /// An array of gossip peers - public Task> Members(bool wan, CancellationToken ct = default(CancellationToken)) - { - var req = _client.Get("/v1/agent/members"); - if (wan) - { - req.Params["wan"] = "1"; - } - return req.Execute(ct); - } - - /// - /// ServiceRegister is used to register a new service with the local agent - /// - /// A service registration object - /// An empty write result - public Task ServiceRegister(AgentServiceRegistration service, CancellationToken ct = default(CancellationToken)) - { - return _client.Put("/v1/agent/service/register", service).Execute(ct); - } - - /// - /// ServiceRegister is used to register a new service with the local agent - /// - /// The service ID - /// An empty write result - public Task ServiceDeregister(string serviceID, CancellationToken ct = default(CancellationToken)) - { - return _client.PutNothing(string.Format("/v1/agent/service/deregister/{0}", serviceID)).Execute(ct); - } - - /// - /// PassTTL is used to set a TTL check to the passing state - /// - /// The check ID - /// An optional, arbitrary string to write to the check status - public Task PassTTL(string checkID, string note, CancellationToken ct = default(CancellationToken)) - { - return LegacyUpdateTTL(checkID, note, TTLStatus.Pass, ct); - } - - /// - /// WarnTTL is used to set a TTL check to the warning state - /// - /// The check ID - /// An optional, arbitrary string to write to the check status - public Task WarnTTL(string checkID, string note, CancellationToken ct = default(CancellationToken)) - { - return LegacyUpdateTTL(checkID, note, TTLStatus.Warn, ct); - } - - /// - /// FailTTL is used to set a TTL check to the failing state - /// - /// The check ID - /// An optional, arbitrary string to write to the check status - public Task FailTTL(string checkID, string note, CancellationToken ct = default(CancellationToken)) - { - return LegacyUpdateTTL(checkID, note, TTLStatus.Critical, ct); - } - - /// - /// UpdateTTL is used to update the TTL of a check - /// - /// The check ID - /// An optional, arbitrary string to write to the check status - /// The state to set the check to - /// An empty write result - public Task UpdateTTL(string checkID, string output, TTLStatus status, CancellationToken ct = default(CancellationToken)) - { - var u = new CheckUpdate - { - Status = status.Status, - Output = output - }; - return _client.Put(string.Format("/v1/agent/check/update/{0}", checkID), u).Execute(ct); - } - - private Task LegacyUpdateTTL(string checkID, string note, TTLStatus status, CancellationToken ct = default(CancellationToken)) - { - var request = _client.PutNothing(string.Format("/v1/agent/check/{0}/{1}", status.LegacyStatus, checkID)); - if (!string.IsNullOrEmpty(note)) - { - request.Params.Add("note", note); - } - return request.Execute(ct); - } - - /// - /// CheckRegister is used to register a new check with the local agent - /// - /// A check registration object - /// An empty write result - public Task CheckRegister(AgentCheckRegistration check, CancellationToken ct = default(CancellationToken)) - { - return _client.Put("/v1/agent/check/register", check).Execute(ct); - } - - /// - /// CheckDeregister is used to deregister a check with the local agent - /// - /// The check ID to deregister - /// An empty write result - public Task CheckDeregister(string checkID, CancellationToken ct = default(CancellationToken)) - { - return _client.PutNothing(string.Format("/v1/agent/check/deregister/{0}", checkID)).Execute(ct); - } - - /// - /// Join is used to instruct the agent to attempt a join to another cluster member - /// - /// The address to join to - /// Join the WAN pool - /// An empty write result - public Task Join(string addr, bool wan, CancellationToken ct = default(CancellationToken)) - { - var req = _client.PutNothing(string.Format("/v1/agent/join/{0}", addr)); - if (wan) - { - req.Params["wan"] = "1"; - } - return req.Execute(ct); - } - - /// - /// ForceLeave is used to have the agent eject a failed node - /// - /// The node name to remove. An attempt to eject a node that doesn't exist will still be successful - /// An empty write result - public Task ForceLeave(string node, CancellationToken ct = default(CancellationToken)) - { - return _client.PutNothing(string.Format("/v1/agent/force-leave/{0}", node)).Execute(ct); - } - - - /// - /// Leave is used to have the agent gracefully leave the cluster and shutdown - /// - /// An empty write result - public Task Leave(string node, CancellationToken ct = default(CancellationToken)) - { - return _client.PutNothing("/v1/agent/leave").Execute(ct); - } - - /// - /// Reload triggers a configuration reload for the agent we are connected to. - /// - /// An empty write result - public Task Reload(string node, CancellationToken ct = default(CancellationToken)) - { - return _client.PutNothing("/v1/agent/reload").Execute(ct); - } - - /// - /// EnableServiceMaintenance toggles service maintenance mode on for the given service ID - /// - /// The service ID - /// An optional reason - /// An empty write result - public Task EnableServiceMaintenance(string serviceID, string reason, CancellationToken ct = default(CancellationToken)) - { - var req = _client.PutNothing(string.Format("/v1/agent/service/maintenance/{0}", serviceID)); - req.Params["enable"] = "true"; - req.Params["reason"] = reason; - return req.Execute(ct); - } - - /// - /// DisableServiceMaintenance toggles service maintenance mode off for the given service ID - /// - /// The service ID - /// An empty write result - public Task DisableServiceMaintenance(string serviceID, CancellationToken ct = default(CancellationToken)) - { - var req = _client.PutNothing(string.Format("/v1/agent/service/maintenance/{0}", serviceID)); - req.Params["enable"] = "false"; - return req.Execute(ct); - } - - /// - /// EnableNodeMaintenance toggles node maintenance mode on for the agent we are connected to - /// - /// An optional reason - /// An empty write result - public Task EnableNodeMaintenance(string reason, CancellationToken ct = default(CancellationToken)) - { - var req = _client.PutNothing("/v1/agent/maintenance"); - req.Params["enable"] = "true"; - req.Params["reason"] = reason; - return req.Execute(ct); - } - - /// - /// DisableNodeMaintenance toggles node maintenance mode off for the agent we are connected to - /// - /// An empty write result - public Task DisableNodeMaintenance(CancellationToken ct = default(CancellationToken)) - { - var req = _client.PutNothing("/v1/agent/maintenance"); - req.Params["enable"] = "false"; - return req.Execute(ct); - } - - /// - /// Monitor yields log lines to display streaming logs from the agent - /// Providing a CancellationToken can be used to close the connection and stop the - /// log stream, otherwise the log stream will time out based on the HTTP Client's timeout value. - /// - public async Task Monitor(LogLevel level = default(LogLevel), CancellationToken ct = default(CancellationToken)) - { - var req = _client.Get("/v1/agent/monitor"); - req.Params["loglevel"] = level.ToString().ToLowerInvariant(); - var res = await req.ExecuteStreaming(ct).ConfigureAwait(false); - return new LogStream(res.Response); - } - - public class LogStream : IEnumerable>, IDisposable - { - private Stream m_stream; - private StreamReader m_streamreader; - internal LogStream(Stream s) - { - m_stream = s; - m_streamreader = new StreamReader(s); - } - - public void Dispose() - { - m_streamreader.Dispose(); - m_stream.Dispose(); - } - - public IEnumerator> GetEnumerator() - { - - while (!m_streamreader.EndOfStream) - { - yield return m_streamreader.ReadLineAsync(); - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - } - - public partial class ConsulClient : IConsulClient - { - private Lazy _agent; - - /// - /// Agent returns a handle to the agent endpoints - /// - public IAgentEndpoint Agent - { - get { return _agent.Value; } - } - } + /// + /// The status of a TTL check + /// + public class TTLStatus : IEquatable + { + private static readonly TTLStatus passingStatus = new TTLStatus() { Status = "passing", LegacyStatus = "pass" }; + private static readonly TTLStatus warningStatus = new TTLStatus() { Status = "warning", LegacyStatus = "warn" }; + private static readonly TTLStatus criticalStatus = new TTLStatus() { Status = "critical", LegacyStatus = "fail" }; + + public string Status { get; private set; } + internal string LegacyStatus { get; private set; } + + public static TTLStatus Pass + { + get { return passingStatus; } + } + + public static TTLStatus Warn + { + get { return warningStatus; } + } + + public static TTLStatus Critical + { + get { return criticalStatus; } + } + + [Obsolete("Use TTLStatus.Critical instead. This status will be an error in 0.7.0+", true)] + public static TTLStatus Fail + { + get { return criticalStatus; } + } + + public bool Equals(TTLStatus other) + { + return other != null && ReferenceEquals(this, other); + } + + public override bool Equals(object other) + { + // other could be a reference type, the is operator will return false if null + return other is TTLStatus && Equals(other as TTLStatus); + } + + public override int GetHashCode() + { + return Status.GetHashCode(); + } + } + + public class TTLStatusConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + serializer.Serialize(writer, ((TTLStatus)value).Status); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, + JsonSerializer serializer) + { + var status = (string)serializer.Deserialize(reader, typeof(string)); + switch (status) + { + case "pass": + return TTLStatus.Pass; + case "passing": + return TTLStatus.Pass; + case "warn": + return TTLStatus.Warn; + case "warning": + return TTLStatus.Warn; + case "fail": + return TTLStatus.Critical; + case "critical": + return TTLStatus.Critical; + default: + throw new ArgumentException("Invalid TTL status value during deserialization"); + } + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(TTLStatus); + } + } + + /// + /// AgentCheck represents a check known to the agent + /// + public class AgentCheck + { + public string Node { get; set; } + public string CheckID { get; set; } + public string Name { get; set; } + + [JsonConverter(typeof(HealthStatusConverter))] + public HealthStatus Status { get; set; } + + public string Notes { get; set; } + public string Output { get; set; } + public string ServiceID { get; set; } + public string ServiceName { get; set; } + } + + /// + /// AgentService represents a service known to the agent + /// + public class AgentService + { + public string ID { get; set; } + public string Service { get; set; } + public string[] Tags { get; set; } + public int Port { get; set; } + public string Address { get; set; } + public bool EnableTagOverride { get; set; } + public IDictionary Meta { get; set; } + } + + /// + /// AgentMember represents a cluster member known to the agent + /// + public class AgentMember + { + public string Name { get; set; } + public string Addr { get; set; } + public ushort Port { get; set; } + public Dictionary Tags { get; set; } + public int Status { get; set; } + public byte ProtocolMin { get; set; } + public byte ProtocolMax { get; set; } + public byte ProtocolCur { get; set; } + public byte DelegateMin { get; set; } + public byte DelegateMax { get; set; } + public byte DelegateCur { get; set; } + + public AgentMember() + { + Tags = new Dictionary(); + } + } + + /// + /// AgentServiceRegistration is used to register a new service + /// + public class AgentServiceRegistration + { + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string ID { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Name { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string[] Tags { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public int Port { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Address { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public bool EnableTagOverride { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public AgentServiceCheck Check { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public AgentServiceCheck[] Checks { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public IDictionary Meta { get; set; } + } + + /// + /// AgentCheckRegistration is used to register a new check + /// + public class AgentCheckRegistration : AgentServiceCheck + { + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string ID { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Name { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Notes { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string ServiceID { get; set; } + } + + /// + /// AgentServiceCheck is used to create an associated check for a service + /// + public class AgentServiceCheck + { + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Script { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string DockerContainerID { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Shell { get; set; } // Only supported for Docker. + + [JsonConverter(typeof(DurationTimespanConverter))] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TimeSpan? Interval { get; set; } + + [JsonConverter(typeof(DurationTimespanConverter))] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TimeSpan? Timeout { get; set; } + + [JsonConverter(typeof(DurationTimespanConverter))] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TimeSpan? TTL { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string HTTP { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string TCP { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string GRPC { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(HealthStatusConverter))] + public HealthStatus Status { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public bool TLSSkipVerify { get; set; } + + /// + /// In Consul 0.7 and later, checks that are associated with a service + /// may also contain this optional DeregisterCriticalServiceAfter field, + /// which is a timeout in the same Go time format as Interval and TTL. If + /// a check is in the critical state for more than this configured value, + /// then its associated service (and all of its associated checks) will + /// automatically be deregistered. + /// + [JsonConverter(typeof(DurationTimespanConverter))] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TimeSpan? DeregisterCriticalServiceAfter { get; set; } + } + + public enum LogLevel + { + Info, + Trace, + Debug, + Warn, + Err + } + + /// + /// Agent can be used to query the Agent endpoints + /// + public class Agent : IAgentEndpoint + { + private class CheckUpdate + { + public string Status { get; set; } + public string Output { get; set; } + } + private readonly ConsulClient _client; + private string _nodeName; + private readonly AsyncLock _nodeNameLock; + + internal Agent(ConsulClient c) + { + _client = c; + _nodeNameLock = new AsyncLock(); + } + + /// + /// Self is used to query the agent we are speaking to for information about itself + /// + /// A somewhat dynamic object representing the various data elements in Self + public Task>>> Self(CancellationToken ct = default(CancellationToken)) + { + return _client.Get>>("/v1/agent/self").Execute(ct); + } + + /// + /// NodeName is used to get the node name of the agent + /// + [Obsolete("This property will be removed in 0.8.0. Replace uses of it with a call to 'await GetNodeName()'")] + public string NodeName + { + get + { + return GetNodeName().ConfigureAwait(false).GetAwaiter().GetResult(); + } + } + + /// + /// GetNodeName is used to get the node name of the agent. The value is cached per instance of ConsulClient after the first use. + /// + public async Task GetNodeName(CancellationToken ct = default(CancellationToken)) + { + if (_nodeName == null) + { + using (await _nodeNameLock.LockAsync().ConfigureAwait(false)) + { + if (_nodeName == null) + { + _nodeName = (await Self(ct)).Response["Config"]["NodeName"]; + } + } + } + + return _nodeName; + } + + /// + /// Checks returns the locally registered checks + /// + /// A map of the registered check names and check data + public Task>> Checks(CancellationToken ct = default(CancellationToken)) + { + return _client.Get>("/v1/agent/checks").Execute(ct); + } + + /// + /// Services returns the locally registered services + /// + /// A map of the registered services and service data + public Task>> Services(CancellationToken ct = default(CancellationToken)) + { + return _client.Get>("/v1/agent/services").Execute(ct); + } + + /// + /// Members returns the known gossip members. The WAN flag can be used to query a server for WAN members. + /// + /// An array of gossip peers + public Task> Members(bool wan, CancellationToken ct = default(CancellationToken)) + { + var req = _client.Get("/v1/agent/members"); + if (wan) + { + req.Params["wan"] = "1"; + } + return req.Execute(ct); + } + + /// + /// ServiceRegister is used to register a new service with the local agent + /// + /// A service registration object + /// An empty write result + public Task ServiceRegister(AgentServiceRegistration service, CancellationToken ct = default(CancellationToken)) + { + return _client.Put("/v1/agent/service/register", service).Execute(ct); + } + + /// + /// ServiceRegister is used to register a new service with the local agent + /// + /// The service ID + /// An empty write result + public Task ServiceDeregister(string serviceID, CancellationToken ct = default(CancellationToken)) + { + return _client.PutNothing(string.Format("/v1/agent/service/deregister/{0}", serviceID)).Execute(ct); + } + + /// + /// PassTTL is used to set a TTL check to the passing state + /// + /// The check ID + /// An optional, arbitrary string to write to the check status + public Task PassTTL(string checkID, string note, CancellationToken ct = default(CancellationToken)) + { + return LegacyUpdateTTL(checkID, note, TTLStatus.Pass, ct); + } + + /// + /// WarnTTL is used to set a TTL check to the warning state + /// + /// The check ID + /// An optional, arbitrary string to write to the check status + public Task WarnTTL(string checkID, string note, CancellationToken ct = default(CancellationToken)) + { + return LegacyUpdateTTL(checkID, note, TTLStatus.Warn, ct); + } + + /// + /// FailTTL is used to set a TTL check to the failing state + /// + /// The check ID + /// An optional, arbitrary string to write to the check status + public Task FailTTL(string checkID, string note, CancellationToken ct = default(CancellationToken)) + { + return LegacyUpdateTTL(checkID, note, TTLStatus.Critical, ct); + } + + /// + /// UpdateTTL is used to update the TTL of a check + /// + /// The check ID + /// An optional, arbitrary string to write to the check status + /// The state to set the check to + /// An empty write result + public Task UpdateTTL(string checkID, string output, TTLStatus status, CancellationToken ct = default(CancellationToken)) + { + var u = new CheckUpdate + { + Status = status.Status, + Output = output + }; + return _client.Put(string.Format("/v1/agent/check/update/{0}", checkID), u).Execute(ct); + } + + private Task LegacyUpdateTTL(string checkID, string note, TTLStatus status, CancellationToken ct = default(CancellationToken)) + { + var request = _client.PutNothing(string.Format("/v1/agent/check/{0}/{1}", status.LegacyStatus, checkID)); + if (!string.IsNullOrEmpty(note)) + { + request.Params.Add("note", note); + } + return request.Execute(ct); + } + + /// + /// CheckRegister is used to register a new check with the local agent + /// + /// A check registration object + /// An empty write result + public Task CheckRegister(AgentCheckRegistration check, CancellationToken ct = default(CancellationToken)) + { + return _client.Put("/v1/agent/check/register", check).Execute(ct); + } + + /// + /// CheckDeregister is used to deregister a check with the local agent + /// + /// The check ID to deregister + /// An empty write result + public Task CheckDeregister(string checkID, CancellationToken ct = default(CancellationToken)) + { + return _client.PutNothing(string.Format("/v1/agent/check/deregister/{0}", checkID)).Execute(ct); + } + + /// + /// Join is used to instruct the agent to attempt a join to another cluster member + /// + /// The address to join to + /// Join the WAN pool + /// An empty write result + public Task Join(string addr, bool wan, CancellationToken ct = default(CancellationToken)) + { + var req = _client.PutNothing(string.Format("/v1/agent/join/{0}", addr)); + if (wan) + { + req.Params["wan"] = "1"; + } + return req.Execute(ct); + } + + /// + /// ForceLeave is used to have the agent eject a failed node + /// + /// The node name to remove. An attempt to eject a node that doesn't exist will still be successful + /// An empty write result + public Task ForceLeave(string node, CancellationToken ct = default(CancellationToken)) + { + return _client.PutNothing(string.Format("/v1/agent/force-leave/{0}", node)).Execute(ct); + } + + + /// + /// Leave is used to have the agent gracefully leave the cluster and shutdown + /// + /// An empty write result + public Task Leave(string node, CancellationToken ct = default(CancellationToken)) + { + return _client.PutNothing("/v1/agent/leave").Execute(ct); + } + + /// + /// Reload triggers a configuration reload for the agent we are connected to. + /// + /// An empty write result + public Task Reload(string node, CancellationToken ct = default(CancellationToken)) + { + return _client.PutNothing("/v1/agent/reload").Execute(ct); + } + + /// + /// EnableServiceMaintenance toggles service maintenance mode on for the given service ID + /// + /// The service ID + /// An optional reason + /// An empty write result + public Task EnableServiceMaintenance(string serviceID, string reason, CancellationToken ct = default(CancellationToken)) + { + var req = _client.PutNothing(string.Format("/v1/agent/service/maintenance/{0}", serviceID)); + req.Params["enable"] = "true"; + req.Params["reason"] = reason; + return req.Execute(ct); + } + + /// + /// DisableServiceMaintenance toggles service maintenance mode off for the given service ID + /// + /// The service ID + /// An empty write result + public Task DisableServiceMaintenance(string serviceID, CancellationToken ct = default(CancellationToken)) + { + var req = _client.PutNothing(string.Format("/v1/agent/service/maintenance/{0}", serviceID)); + req.Params["enable"] = "false"; + return req.Execute(ct); + } + + /// + /// EnableNodeMaintenance toggles node maintenance mode on for the agent we are connected to + /// + /// An optional reason + /// An empty write result + public Task EnableNodeMaintenance(string reason, CancellationToken ct = default(CancellationToken)) + { + var req = _client.PutNothing("/v1/agent/maintenance"); + req.Params["enable"] = "true"; + req.Params["reason"] = reason; + return req.Execute(ct); + } + + /// + /// DisableNodeMaintenance toggles node maintenance mode off for the agent we are connected to + /// + /// An empty write result + public Task DisableNodeMaintenance(CancellationToken ct = default(CancellationToken)) + { + var req = _client.PutNothing("/v1/agent/maintenance"); + req.Params["enable"] = "false"; + return req.Execute(ct); + } + + /// + /// Monitor yields log lines to display streaming logs from the agent + /// Providing a CancellationToken can be used to close the connection and stop the + /// log stream, otherwise the log stream will time out based on the HTTP Client's timeout value. + /// + public async Task Monitor(LogLevel level = default(LogLevel), CancellationToken ct = default(CancellationToken)) + { + var req = _client.Get("/v1/agent/monitor"); + req.Params["loglevel"] = level.ToString().ToLowerInvariant(); + var res = await req.ExecuteStreaming(ct).ConfigureAwait(false); + return new LogStream(res.Response); + } + + public class LogStream : IEnumerable>, IDisposable + { + private Stream m_stream; + private StreamReader m_streamreader; + internal LogStream(Stream s) + { + m_stream = s; + m_streamreader = new StreamReader(s); + } + + public void Dispose() + { + m_streamreader.Dispose(); + m_stream.Dispose(); + } + + public IEnumerator> GetEnumerator() + { + + while (!m_streamreader.EndOfStream) + { + yield return m_streamreader.ReadLineAsync(); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + } + + public partial class ConsulClient : IConsulClient + { + private Lazy _agent; + + /// + /// Agent returns a handle to the agent endpoints + /// + public IAgentEndpoint Agent + { + get { return _agent.Value; } + } + } }