From 267880ffa2e7119a20441e94fc39ce125232ae5e Mon Sep 17 00:00:00 2001 From: Jamesxql Date: Sun, 12 Nov 2017 07:29:19 +0800 Subject: [PATCH 1/6] add Rfc3339DateTimeConverter --- Consul/Utilities/JsonConverters.cs | 53 ++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/Consul/Utilities/JsonConverters.cs b/Consul/Utilities/JsonConverters.cs index 6018f02..e53fcd4 100644 --- a/Consul/Utilities/JsonConverters.cs +++ b/Consul/Utilities/JsonConverters.cs @@ -18,12 +18,59 @@ using System; using Newtonsoft.Json; -using System.Reflection; -using System.Linq; -using System.Collections.Generic; +using System.Globalization; namespace Consul { + public class Rfc3339DateTimeConverter : JsonConverter + { + private const string Rfc3339DateTimePattern1 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK"; + private const string Rfc3339DateTimePattern2 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffffK"; + private const string Rfc3339DateTimePattern3 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffK"; + private const string Rfc3339DateTimePattern4 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffK"; + private const string Rfc3339DateTimePattern5 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK"; + private const string Rfc3339DateTimePattern6 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffK"; + private const string Rfc3339DateTimePattern7 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fK"; + + private static readonly string[] Formats = + { + Rfc3339DateTimePattern1, Rfc3339DateTimePattern2, Rfc3339DateTimePattern3, Rfc3339DateTimePattern4, + Rfc3339DateTimePattern5, Rfc3339DateTimePattern6, Rfc3339DateTimePattern7 + }; + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + serializer.Serialize(writer, + ((DateTime) value).ToString(Rfc3339DateTimePattern1, DateTimeFormatInfo.InvariantInfo), typeof(string)); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var value = (string) serializer.Deserialize(reader, typeof(string)); + + foreach (var format in Formats) + { + if (DateTime.TryParseExact(value, format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeLocal, + out var result)) + { + return result; + } + } + + throw new FormatException(string.Format(CultureInfo.InvariantCulture, + "{0} is not a valid RFC 3339 string representation of a date and time.", value)); + } + + public override bool CanConvert(Type objectType) + { + if (objectType == typeof(DateTime)) + { + return true; + } + return false; + } + } + public class NanoSecTimespanConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) From b825cde62d87dd65b53daefc453363c609833f5d Mon Sep 17 00:00:00 2001 From: Jamesxql Date: Sun, 12 Nov 2017 07:31:20 +0800 Subject: [PATCH 2/6] add Rfc3339DateTimeConverter to ACLReplicationStatus class --- Consul/ACL.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Consul/ACL.cs b/Consul/ACL.cs index a97967e..7a244ca 100644 --- a/Consul/ACL.cs +++ b/Consul/ACL.cs @@ -137,7 +137,9 @@ public class ACLReplicationStatus public bool Running { get; set; } public string SourceDatacenter { get; set; } public ulong ReplicatedIndex { get; set; } + [JsonConverter(typeof(Rfc3339DateTimeConverter))] public DateTime LastSuccess { get; set; } + [JsonConverter(typeof(Rfc3339DateTimeConverter))] public DateTime LastError { get; set; } } From 4cf4bbcd2651f7d4c709448970c54db3cbcd48d2 Mon Sep 17 00:00:00 2001 From: Jamesxql Date: Sun, 12 Nov 2017 07:45:32 +0800 Subject: [PATCH 3/6] Add all ACL token update methods --- Consul/Agent.cs | 130 ++++++++++++++++++++++++++++ Consul/Interfaces/IAgentEndpoint.cs | 8 ++ 2 files changed, 138 insertions(+) diff --git a/Consul/Agent.cs b/Consul/Agent.cs index 4c826d7..1380882 100644 --- a/Consul/Agent.cs +++ b/Consul/Agent.cs @@ -358,6 +358,9 @@ private class CheckUpdate public string Output { get; set; } } + /// + /// AgentToken is used when updating ACL tokens for an agent. + /// private class AgentToken { [JsonProperty] @@ -665,6 +668,133 @@ internal Agent(ConsulClient c) var res = await req.ExecuteStreaming(ct).ConfigureAwait(false); return new LogStream(res.Response); } + + /// + /// UpdateACLToken updates the agent's "acl_token". + /// for more details. + /// + /// + /// + /// + /// + /// + public Task UpdateACLToken(string token, WriteOptions q, CancellationToken ct = default(CancellationToken)) + { + return UpdateToken("acl_token", token, q, ct); + } + + /// + /// UpdateACLToken updates the agent's "acl_token". + /// for more details. + /// + /// + /// + /// + /// + public Task UpdateACLToken(string token, CancellationToken ct = default(CancellationToken)) + { + return UpdateACLToken(token, WriteOptions.Default, ct); + } + + /// + /// UpdateACLAgentToken updates the agent's "acl_agent_token". + /// for more details. + /// + /// + /// + /// + /// + /// + public Task UpdateACLAgentToken(string token, WriteOptions q, CancellationToken ct = default(CancellationToken)) + { + return UpdateToken("acl_agent_token", token, q, ct); + } + + /// + /// UpdateACLAgentToken updates the agent's "acl_agent_token". + /// for more details. + /// + /// + /// + /// + /// + public Task UpdateACLAgentToken(string token, CancellationToken ct = default(CancellationToken)) + { + return UpdateACLAgentToken(token, WriteOptions.Default, ct); + } + + /// + /// UpdateACLAgentMasterToken updates the agent's "acl_agent_master_token". + /// for more details. + /// + /// + /// + /// + /// + /// + public Task UpdateACLAgentMasterToken(string token, WriteOptions q, CancellationToken ct = default(CancellationToken)) + { + return UpdateToken("acl_agent_master_token", token, q, ct); + } + + /// + /// UpdateACLAgentMasterToken updates the agent's "acl_agent_master_token". + /// for more details. + /// + /// + /// + /// + /// + public Task UpdateACLAgentMasterToken(string token, CancellationToken ct = default(CancellationToken)) + { + return UpdateACLAgentMasterToken(token, WriteOptions.Default, ct); + } + + /// + /// UpdateACLReplicationToken updates the agent's "acl_replication_token". + /// for more details. + /// + /// + /// + /// + /// + /// + public Task UpdateACLReplicationToken(string token, WriteOptions q, CancellationToken ct = default(CancellationToken)) + { + return UpdateToken("acl_replication_token", token, q, ct); + } + + /// + /// UpdateACLReplicationToken updates the agent's "acl_replication_token". + /// for more details. + /// + /// + /// + /// + /// + /// + public Task UpdateACLReplicationToken(string token, CancellationToken ct = default(CancellationToken)) + { + return UpdateACLReplicationToken(token, WriteOptions.Default, ct); + } + + /// + /// UpdateToken can be used to update an agent's ACL token after the agent has + /// started. The tokens are not persisted, so will need to be updated again if + /// the agent is restarted. + /// + /// + /// + /// + /// + /// + private Task UpdateToken(string target, string token, WriteOptions q, CancellationToken ct) + { + var req = _client.Put(string.Format("/v1/agent/token/{0}", target), + new AgentToken {Token = token}, q); + + return req.Execute(ct); + } public class LogStream : IEnumerable>, IDisposable { diff --git a/Consul/Interfaces/IAgentEndpoint.cs b/Consul/Interfaces/IAgentEndpoint.cs index cc363e5..ff4041f 100644 --- a/Consul/Interfaces/IAgentEndpoint.cs +++ b/Consul/Interfaces/IAgentEndpoint.cs @@ -49,5 +49,13 @@ public interface IAgentEndpoint Task Leave(string node, CancellationToken ct = default(CancellationToken)); Task Reload(CancellationToken ct = default(CancellationToken)); Task> Metrics(CancellationToken ct = default(CancellationToken)); + Task UpdateACLToken(string token, WriteOptions q, CancellationToken ct = default(CancellationToken)); + Task UpdateACLToken(string token, CancellationToken ct = default(CancellationToken)); + Task UpdateACLAgentToken(string token, WriteOptions q, CancellationToken ct = default(CancellationToken)); + Task UpdateACLAgentToken(string token, CancellationToken ct = default(CancellationToken)); + Task UpdateACLAgentMasterToken(string token, WriteOptions q, CancellationToken ct = default(CancellationToken)); + Task UpdateACLAgentMasterToken(string token, CancellationToken ct = default(CancellationToken)); + Task UpdateACLReplicationToken(string token, WriteOptions q, CancellationToken ct = default(CancellationToken)); + Task UpdateACLReplicationToken(string token, CancellationToken ct = default(CancellationToken)); } } From e9e1fed366feaae763a61112d2cad40b6650e181 Mon Sep 17 00:00:00 2001 From: Jamesxql Date: Sun, 12 Nov 2017 08:17:58 +0800 Subject: [PATCH 4/6] Add all new API in Operator object --- Consul/Interfaces/IOperatorEndpoint.cs | 24 + Consul/Operator.cs | 603 +++++++++++++++++++++++++ 2 files changed, 627 insertions(+) diff --git a/Consul/Interfaces/IOperatorEndpoint.cs b/Consul/Interfaces/IOperatorEndpoint.cs index 61af316..fbc9023 100644 --- a/Consul/Interfaces/IOperatorEndpoint.cs +++ b/Consul/Interfaces/IOperatorEndpoint.cs @@ -20,5 +20,29 @@ public interface IOperatorEndpoint Task KeyringRemove(string key, WriteOptions q, CancellationToken ct = default(CancellationToken)); Task KeyringUse(string key, CancellationToken ct = default(CancellationToken)); Task KeyringUse(string key, WriteOptions q, CancellationToken ct = default(CancellationToken)); + Task> AreaCreate(Area area, WriteOptions q, CancellationToken ct = default(CancellationToken)); + Task> AreaCreate(Area area, CancellationToken ct = default(CancellationToken)); + Task AreaDelete(string areaID, WriteOptions q, CancellationToken ct = default(CancellationToken)); + Task AreaDelete(string areaID, CancellationToken ct = default(CancellationToken)); + Task> AreaGet(string areaID, QueryOptions q, CancellationToken ct = default(CancellationToken)); + Task> AreaGet(string areaID, CancellationToken ct = default(CancellationToken)); + Task> AreaJoin(string areaID, string[] addresses, WriteOptions q, CancellationToken ct = default(CancellationToken)); + Task> AreaJoin(string areaID, string[] addresses, CancellationToken ct = default(CancellationToken)); + Task> AreaList(QueryOptions q, CancellationToken ct = default(CancellationToken)); + Task> AreaList(CancellationToken ct = default(CancellationToken)); + Task> AreaMembers(string areaID, QueryOptions q, CancellationToken ct = default(CancellationToken)); + Task> AreaMembers(string areaID, CancellationToken ct = default(CancellationToken)); + Task> AreaUpdate(string areaID, Area area, WriteOptions q, CancellationToken ct = default (CancellationToken)); + Task> AreaUpdate(string areaID, Area area, CancellationToken ct = default (CancellationToken)); + Task> SegmentList(QueryOptions q, CancellationToken ct = default(CancellationToken)); + Task> SegmentList(CancellationToken ct = default(CancellationToken)); + Task AutopilotCASConfiguration(AutopilotConfiguration conf, WriteOptions q, CancellationToken ct = default(CancellationToken)); + Task AutopilotCASConfiguration(AutopilotConfiguration conf, CancellationToken ct = default(CancellationToken)); + Task AutopilotGetConfiguration(QueryOptions q, CancellationToken ct = default(CancellationToken)); + Task AutopilotGetConfiguration(CancellationToken ct = default(CancellationToken)); + Task AutopilotSetConfiguration(AutopilotConfiguration conf, WriteOptions q, CancellationToken ct = default(CancellationToken)); + Task AutopilotSetConfiguration(AutopilotConfiguration conf, CancellationToken ct = default(CancellationToken)); + Task AutopilotServerHealth(QueryOptions q, CancellationToken ct = default(CancellationToken)); + Task AutopilotServerHealth(CancellationToken ct = default(CancellationToken)); } } diff --git a/Consul/Operator.cs b/Consul/Operator.cs index dd49b4e..3a8a321 100644 --- a/Consul/Operator.cs +++ b/Consul/Operator.cs @@ -1,9 +1,11 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Newtonsoft.Json.Linq; namespace Consul { @@ -82,6 +84,277 @@ public class KeyringResponse /// public int NumNodes { get; set; } } + + /// + /// Area defines a network area. + /// + public class Area + { + /// + /// ID is this identifier for an area (a UUID). This must be left empty + /// when creating a new area. + /// + public string ID { get; set; } + + /// + /// PeerDatacenter is the peer Consul datacenter that will make up the + /// other side of this network area. Network areas always involve a pair + /// of datacenters: the datacenter where the area was created, and the + /// peer datacenter. This is required. + /// + public string PeerDatacenter { get; set; } + + /// + /// RetryJoin specifies the address of Consul servers to join to, such as + /// an IPs or hostnames with an optional port number. This is optional. + /// + public string[] RetryJoin { get; set; } + } + + /// + /// AreaJoinResponse is returned when a join occurs and gives the result for each + /// address. + /// + public class AreaJoinResponse + { + /// + /// The address that was joined. + /// + public string Address { get; set; } + + /// + /// Whether or not the join was a success. + /// + public bool Joined { get; set; } + + /// + /// If we couldn't join, this is the message with information. + /// + public string Error { get; set; } + } + + /// + /// SerfMember is a generic structure for reporting information about members in + /// a Serf cluster. This is only used by the area endpoints right now, but this + /// could be expanded to other endpoints in the future. + /// + public class SerfMember + { + /// + /// ID is the node identifier (a UUID). + /// + public string ID { get; set; } + + /// + /// Name is the node name. + /// + public string Name { get; set; } + + /// + /// Addr has the IP address. + /// + public string Addr { get; set; } + + /// + /// Port is the RPC port. + /// + public ushort Port { get; set; } + + /// + /// Datacenter is the DC name. + /// + public string Datacenter { get; set; } + + /// + /// Role is "client", "server", or "unknown". + /// + public string Role { get; set; } + + /// + /// Build has the version of the Consul agent. + /// + public string Build { get; set; } + + /// + /// Protocol is the protocol of the Consul agent. + /// + public int Protocol { get; set; } + + /// + /// Status is the Serf health status "none", "alive", "leaving", "left", + /// or "failed". + /// + public string Status { get; set; } + + /// + /// RTT is the estimated round trip time from the server handling the + /// request to the this member. This will be negative if no RTT estimate + /// is available. + /// + [JsonConverter(typeof(NanoSecTimespanConverter))] + public TimeSpan RTT { get; set; } + } + + /// + /// AutopilotConfiguration is used for querying/setting the Autopilot configuration. + /// Autopilot helps manage operator tasks related to Consul servers like removing + /// failed servers from the Raft quorum. + /// + public class AutopilotConfiguration + { + /// + /// CleanupDeadServers controls whether to remove dead servers from the Raft + /// peer list when a new server joins + /// + public bool CleanupDeadServers { get; set; } + + /// + /// LastContactThreshold is the limit on the amount of time a server can go + /// without leader contact before being considered unhealthy. + /// + [JsonConverter(typeof(DurationTimespanConverter))] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TimeSpan? LastContactThreshold { get; set; } + + /// + /// MaxTrailingLogs is the amount of entries in the Raft Log that a server can + /// be behind before being considered unhealthy. + /// + public ulong MaxTrailingLogs { get; set; } + + /// + /// ServerStabilizationTime is the minimum amount of time a server must be + /// in a stable, healthy state before it can be added to the cluster. Only + /// applicable with Raft protocol version 3 or higher. + /// + [JsonConverter(typeof(DurationTimespanConverter))] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TimeSpan? ServerStabilizationTime { get; set; } + + /// + /// (Enterprise-only) RedundancyZoneTag is the node tag to use for separating + /// servers into zones for redundancy. If left blank, this feature will be disabled. + /// + public string RedundancyZoneTag { get; set; } + + /// + /// (Enterprise-only) DisableUpgradeMigration will disable Autopilot's upgrade migration + /// strategy of waiting until enough newer-versioned servers have been added to the + /// cluster before promoting them to voters. + /// + public bool DisableUpgradeMigration { get; set; } + + /// + /// (Enterprise-only) UpgradeVersionTag is the node tag to use for version info when + /// performing upgrade migrations. If left blank, the Consul version will be used. + /// + public string UpgradeVersionTag { get; set; } + + /// + /// CreateIndex holds the index corresponding the creation of this configuration. + /// This is a read-only field. + /// + public ulong CreateIndex { get; set; } + + /// + /// ModifyIndex will be set to the index of the last update when retrieving the + /// Autopilot configuration. Resubmitting a configuration with + /// AutopilotCASConfiguration will perform a check-and-set operation which ensures + /// there hasn't been a subsequent update since the configuration was retrieved. + /// + public ulong ModifyIndex { get; set; } + } + + /// + /// ServerHealth is the health (from the leader's point of view) of a server. + /// + public class ServerHealth + { + /// + /// ID is the raft ID of the server. + /// + public string ID { get; set; } + + /// + /// Name is the node name of the server. + /// + public string Name { get; set; } + + /// + /// Address is the address of the server. + /// + public string Address { get; set; } + + /// + /// The status of the SerfHealth check for the server. + /// + public string SerfStatus { get; set; } + + /// + /// Version is the Consul version of the server. + /// + public string Version { get; set; } + + /// + /// Leader is whether this server is currently the leader. + /// + public string Leader { get; set; } + + /// + /// LastContact is the time since this node's last contact with the leader. + /// + [JsonConverter(typeof(DurationTimespanConverter))] + public TimeSpan LastContact { get; set; } + + /// + /// LastTerm is the highest leader term this server has a record of in its Raft log. + /// + public ulong LastTerm { get; set; } + + /// + /// LastIndex is the last log index this server has a record of in its Raft log. + /// + public ulong LastIndex { get; set; } + + /// + /// Healthy is whether or not the server is healthy according to the current + /// Autopilot config. + /// + public bool Healthy { get; set; } + + /// + /// Voter is whether this is a voting server. + /// + public bool Voter { get; set; } + + /// + /// StableSince is the last time this server's Healthy value changed. + /// + [JsonConverter(typeof(Rfc3339DateTimeConverter))] + public DateTime StableSince { get; set; } + } + + /// + /// OperatorHealthReply is a representation of the overall health of the cluster + /// + public class OperatorHealthReply + { + /// + /// Healthy is true if all the servers in the cluster are healthy. + /// + public bool Healthy { get; set; } + + /// + /// FailureTolerance is the number of healthy servers that could be lost without + /// an outage occurring. + /// + public int FailureTolerance { get; set; } + + /// + /// Servers holds the health of each server. + /// + public ServerHealth[] Servers { get; set; } + } public class Operator : IOperatorEndpoint { @@ -95,6 +368,12 @@ internal Operator(ConsulClient c) { _client = c; } + + private class AreaCreationResult + { + [JsonProperty] + internal string ID { get; set; } + } /// /// KeyringRequest is used for performing Keyring operations @@ -211,6 +490,330 @@ private class KeyringRequest { return _client.Put("/v1/operator/keyring", new KeyringRequest() { Key = key }, q).Execute(ct); } + + /// + /// AreaCreate will create a new network area. The ID in the given structure must + /// be empty and a generated ID will be returned on success. + /// + /// + /// + /// + /// + /// + public async Task> AreaCreate(Area area, WriteOptions q, CancellationToken ct = default(CancellationToken)) + { + var res = await _client.Post("/v1/operator/area", area, q).Execute(ct) + .ConfigureAwait(false); + + return new WriteResult(res, res.Response.ID); + } + + /// + /// AreaCreate will create a new network area. The ID in the given structure must + /// be empty and a generated ID will be returned on success. + /// + /// + /// + /// + /// + public Task> AreaCreate(Area area, CancellationToken ct = default(CancellationToken)) + { + return AreaCreate(area, WriteOptions.Default, ct); + } + + /// + /// AreaDelete deletes the given network area. + /// + /// + /// + /// + /// + /// + public Task AreaDelete(string areaID, WriteOptions q, CancellationToken ct = default(CancellationToken)) + { + return _client.Delete(string.Format("/v1/operator/area/{0}", areaID)).Execute(ct); + } + + /// + /// AreaDelete deletes the given network area. + /// + /// + /// + /// + /// + public Task AreaDelete(string areaID, CancellationToken ct = default(CancellationToken)) + { + return AreaDelete(areaID, WriteOptions.Default, ct); + } + + /// + /// AreaGet returns a single network area. + /// + /// + /// + /// + /// + /// + public Task> AreaGet(string areaID, QueryOptions q, CancellationToken ct = default(CancellationToken)) + { + return _client.Get(string.Format("/v1/operator/area/{0}", areaID), q).Execute(ct); + } + + /// + /// AreaGet returns a single network area. + /// + /// + /// + /// + /// + public Task> AreaGet(string areaID, CancellationToken ct = default(CancellationToken)) + { + return AreaGet(areaID, QueryOptions.Default, ct); + } + + /// + /// AreaJoin attempts to join the given set of join addresses to the given + /// network area. See the Area class for details about join addresses. + /// + /// + /// + /// + /// + /// + /// + public Task> AreaJoin(string areaID, string[] addresses, WriteOptions q, CancellationToken ct = default(CancellationToken)) + { + return _client + .Put(string.Format("/v1/operator/area/{0}/join", areaID), addresses, q) + .Execute(ct); + } + + /// + /// AreaJoin attempts to join the given set of join addresses to the given + /// network area. See the Area class for details about join addresses. + /// + /// + /// + /// + /// + /// + public Task> AreaJoin(string areaID, string[] addresses, CancellationToken ct = default(CancellationToken)) + { + return AreaJoin(areaID, addresses, WriteOptions.Default, ct); + } + + /// + /// AreaList returns all the available network areas. + /// + /// + /// + /// + /// + public Task> AreaList(QueryOptions q, CancellationToken ct = default(CancellationToken)) + { + return _client.Get("/v1/operator/area").Execute(ct); + } + + /// + /// AreaList returns all the available network areas. + /// + /// + /// + /// + public Task> AreaList(CancellationToken ct = default(CancellationToken)) + { + return AreaList(QueryOptions.Default, ct); + } + + /// + /// AreaMembers lists the Serf information about the members in the given area. + /// + /// + /// + /// + /// + /// + public Task> AreaMembers(string areaID, QueryOptions q, CancellationToken ct = default(CancellationToken)) + { + return _client.Get(string.Format("/v1/operator/area/{0}/members", areaID), q).Execute(ct); + } + + /// + /// AreaMembers lists the Serf information about the members in the given area. + /// + /// + /// + /// + /// + public Task> AreaMembers(string areaID, CancellationToken ct = default(CancellationToken)) + { + return AreaMembers(areaID, QueryOptions.Default, ct); + } + + /// + /// AreaUpdate will update the configuration of the network area with the given ID. + /// + /// + /// + /// + /// + /// + /// + public async Task> AreaUpdate(string areaID, Area area, WriteOptions q, CancellationToken ct = default(CancellationToken)) + { + var res = await _client.Put(string.Format("/v1/operator/area/{0}", areaID), area, q) + .Execute(ct).ConfigureAwait(false); + + return new WriteResult(res, res.Response.ID); + } + + /// + /// AreaUpdate will update the configuration of the network area with the given ID. + /// + /// + /// + /// + /// + /// + public Task> AreaUpdate(string areaID, Area area, CancellationToken ct = default(CancellationToken)) + { + return AreaUpdate(areaID, area, WriteOptions.Default, ct); + } + + /// + /// SegmentList returns all the available LAN segments. + /// + /// + /// + /// + /// + public Task> SegmentList(QueryOptions q, CancellationToken ct = default(CancellationToken)) + { + return _client.Get("/v1/operator/segment", q).Execute(ct); + } + + /// + /// SegmentList returns all the available LAN segments. + /// + /// + /// + /// + public Task> SegmentList(CancellationToken ct = default(CancellationToken)) + { + return SegmentList(QueryOptions.Default, ct); + } + + /// + /// AutopilotCASConfiguration is used to perform a Check-And-Set update on the + /// Autopilot configuration. The ModifyIndex value will be respected. Returns + /// true on success or false on failures. + /// + /// + /// + /// + /// + public async Task AutopilotCASConfiguration(AutopilotConfiguration conf, WriteOptions q, + CancellationToken ct = default(CancellationToken)) + { + //TODO: Maybe there is a better way to execute the request + var req = _client.Put("/v1/operator/autopilot/configuration", conf, q); + req.Params["cas"] = conf.ModifyIndex.ToString(); + + await req.Execute(ct).ConfigureAwait(false); + + using (var reader = new StreamReader(req.ResponseStream)) + { + var body = await reader.ReadToEndAsync().ConfigureAwait(false); + return body.Contains("true"); + } + } + + /// + /// AutopilotCASConfiguration is used to perform a Check-And-Set update on the + /// Autopilot configuration. The ModifyIndex value will be respected. Returns + /// true on success or false on failures. + /// + /// + /// + /// + public Task AutopilotCASConfiguration(AutopilotConfiguration conf, CancellationToken ct = default(CancellationToken)) + { + return AutopilotCASConfiguration(conf, WriteOptions.Default, ct); + } + + /// + /// AutopilotGetConfiguration is used to query the current Autopilot configuration. + /// + /// + /// + /// + /// + public async Task AutopilotGetConfiguration(QueryOptions q, CancellationToken ct = default(CancellationToken)) + { + var res = await _client.Get("/v1/operator/autopilot/configuration", q).Execute(ct).ConfigureAwait(false); + return res.Response; + } + + /// + /// AutopilotGetConfiguration is used to query the current Autopilot configuration. + /// + /// + /// + /// + public Task AutopilotGetConfiguration(CancellationToken ct = default(CancellationToken)) + { + return AutopilotGetConfiguration(QueryOptions.Default, ct); + } + + /// + /// AutopilotSetConfiguration is used to set the current Autopilot configuration. + /// + /// + /// + /// + /// + /// + public async Task AutopilotSetConfiguration(AutopilotConfiguration conf, WriteOptions q, + CancellationToken ct = default(CancellationToken)) + { + await _client.Put("/v1/operator/autopilot/configuration", conf, q).Execute(ct).ConfigureAwait(false); + } + + /// + /// AutopilotSetConfiguration is used to set the current Autopilot configuration. + /// + /// + /// + /// + /// + public Task AutopilotSetConfiguration(AutopilotConfiguration conf, CancellationToken ct = default(CancellationToken)) + { + return AutopilotSetConfiguration(conf, WriteOptions.Default, ct); + } + + /// + /// AutopilotServerHealth + /// + /// + /// + /// + /// + public async Task AutopilotServerHealth(QueryOptions q, CancellationToken ct = default(CancellationToken)) + { + var resp = await _client.Get("/v1/operator/autopilot/health", q).Execute(ct).ConfigureAwait(false); + return resp.Response; + } + + /// + /// AutopilotServerHealth + /// + /// + /// + /// + public Task AutopilotServerHealth(CancellationToken ct = default(CancellationToken)) + { + return AutopilotServerHealth(QueryOptions.Default, ct); + } } public partial class ConsulClient : IConsulClient From 5b7b538a7d11b5fc593c737fb350261363b9997a Mon Sep 17 00:00:00 2001 From: Jamesxql Date: Sun, 12 Nov 2017 08:34:20 +0800 Subject: [PATCH 5/6] Remove Rfc3339DateTimeConverter, it's unnecessary --- Consul/ACL.cs | 2 -- Consul/Operator.cs | 1 - Consul/Utilities/JsonConverters.cs | 49 ------------------------------ 3 files changed, 52 deletions(-) diff --git a/Consul/ACL.cs b/Consul/ACL.cs index 7a244ca..a97967e 100644 --- a/Consul/ACL.cs +++ b/Consul/ACL.cs @@ -137,9 +137,7 @@ public class ACLReplicationStatus public bool Running { get; set; } public string SourceDatacenter { get; set; } public ulong ReplicatedIndex { get; set; } - [JsonConverter(typeof(Rfc3339DateTimeConverter))] public DateTime LastSuccess { get; set; } - [JsonConverter(typeof(Rfc3339DateTimeConverter))] public DateTime LastError { get; set; } } diff --git a/Consul/Operator.cs b/Consul/Operator.cs index 3a8a321..970889d 100644 --- a/Consul/Operator.cs +++ b/Consul/Operator.cs @@ -330,7 +330,6 @@ public class ServerHealth /// /// StableSince is the last time this server's Healthy value changed. /// - [JsonConverter(typeof(Rfc3339DateTimeConverter))] public DateTime StableSince { get; set; } } diff --git a/Consul/Utilities/JsonConverters.cs b/Consul/Utilities/JsonConverters.cs index e53fcd4..7c106e0 100644 --- a/Consul/Utilities/JsonConverters.cs +++ b/Consul/Utilities/JsonConverters.cs @@ -22,55 +22,6 @@ namespace Consul { - public class Rfc3339DateTimeConverter : JsonConverter - { - private const string Rfc3339DateTimePattern1 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK"; - private const string Rfc3339DateTimePattern2 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffffK"; - private const string Rfc3339DateTimePattern3 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffK"; - private const string Rfc3339DateTimePattern4 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffK"; - private const string Rfc3339DateTimePattern5 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK"; - private const string Rfc3339DateTimePattern6 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffK"; - private const string Rfc3339DateTimePattern7 = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fK"; - - private static readonly string[] Formats = - { - Rfc3339DateTimePattern1, Rfc3339DateTimePattern2, Rfc3339DateTimePattern3, Rfc3339DateTimePattern4, - Rfc3339DateTimePattern5, Rfc3339DateTimePattern6, Rfc3339DateTimePattern7 - }; - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - serializer.Serialize(writer, - ((DateTime) value).ToString(Rfc3339DateTimePattern1, DateTimeFormatInfo.InvariantInfo), typeof(string)); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var value = (string) serializer.Deserialize(reader, typeof(string)); - - foreach (var format in Formats) - { - if (DateTime.TryParseExact(value, format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeLocal, - out var result)) - { - return result; - } - } - - throw new FormatException(string.Format(CultureInfo.InvariantCulture, - "{0} is not a valid RFC 3339 string representation of a date and time.", value)); - } - - public override bool CanConvert(Type objectType) - { - if (objectType == typeof(DateTime)) - { - return true; - } - return false; - } - } - public class NanoSecTimespanConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) From b485783552214932a99e2f575fa944b00952a700 Mon Sep 17 00:00:00 2001 From: Jamesxql Date: Sun, 12 Nov 2017 16:07:53 +0800 Subject: [PATCH 6/6] Upgrade consul version in appveyor.yml script --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f5a9911..a588f12 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,7 +15,7 @@ assembly_info: assembly_informational_version: $(APPVEYOR_BUILD_VERSION) environment: CONSUL_BIN: c:\consul\consul.test\consul.exe - CONSUL_VERSION: 0.7.2 + CONSUL_VERSION: 0.9.3 CLI_VERSION: Latest install: - cinst: 7zip.commandline