diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bdb0cab --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore index fd5204b..78b49ee 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # User-specific files *.suo *.user +*.userosscache *.sln.docstates # Build results @@ -181,3 +182,50 @@ UpgradeLog*.htm # Microsoft Fakes FakesAssemblies/ + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk diff --git a/Draft.sln b/Draft.sln new file mode 100644 index 0000000..fd15bfb --- /dev/null +++ b/Draft.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Draft-Net45", "source\Draft\Draft-Net45.csproj", "{A4D60581-25BF-4DE9-877C-4179B3F0747C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Draft-Net45.Tests", "tests\Draft.Tests\Draft-Net45.Tests.csproj", "{3F288474-3C7C-4AFA-BE4F-794F32720F9A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{EC663AEE-1976-42DC-94DE-3046D3CE16AD}" + ProjectSection(SolutionItems) = preProject + meta\Draft.nuspec = meta\Draft.nuspec + LICENSE.txt = LICENSE.txt + README.md = README.md + ReleaseNotes.md = ReleaseNotes.md + meta\SolutionInfo.cs = meta\SolutionInfo.cs + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A4D60581-25BF-4DE9-877C-4179B3F0747C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4D60581-25BF-4DE9-877C-4179B3F0747C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4D60581-25BF-4DE9-877C-4179B3F0747C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4D60581-25BF-4DE9-877C-4179B3F0747C}.Release|Any CPU.Build.0 = Release|Any CPU + {3F288474-3C7C-4AFA-BE4F-794F32720F9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F288474-3C7C-4AFA-BE4F-794F32720F9A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F288474-3C7C-4AFA-BE4F-794F32720F9A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F288474-3C7C-4AFA-BE4F-794F32720F9A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/ReleaseNotes.md b/ReleaseNotes.md new file mode 100644 index 0000000..4688615 --- /dev/null +++ b/ReleaseNotes.md @@ -0,0 +1,2 @@ +### New in 0.1.0 (Released 2015/02/20) +* Initial release diff --git a/build.csx b/build.csx new file mode 100644 index 0000000..91bc6ae --- /dev/null +++ b/build.csx @@ -0,0 +1,211 @@ +/////////////////////////////////////////////////////////////////////////////// +// ARGUMENTS +/////////////////////////////////////////////////////////////////////////////// + +var target = Argument("target", "Default"); +var configuration = Argument("configuration", "Debug"); +var forcePackage = HasArgument("forcePackage"); + +/////////////////////////////////////////////////////////////////////////////// +// GLOBAL VARIABLES +/////////////////////////////////////////////////////////////////////////////// + +var projectName = "Draft"; + +// "Root" +var context = GetContext(); +var baseDir = context.Environment.WorkingDirectory; +var solution = baseDir.GetFilePath(projectName + ".sln"); + +// Directories +// WorkingDirectory is relative to this file. Make it relative to the Solution file. +var solutionDir = solution.GetDirectory(); +var packagingRoot = baseDir.Combine("publish"); +var testResultsDir = baseDir.Combine("TestResults"); +var nugetPackagingDir = packagingRoot.Combine(projectName); +var sourcesDir = solutionDir.Combine("src"); +var testsDir = solutionDir.Combine("tests"); +var metaDir = solutionDir.Combine("meta"); + +// Files +var solutionInfoCs = metaDir.GetFilePath("SolutionInfo.cs"); +var nuspecFile = metaDir.GetFilePath(projectName + ".nuspec"); +var licenseFile = solutionDir.GetFilePath("LICENSE.txt"); +var readmeFile = solutionDir.GetFilePath("README.md"); +var releaseNotesFile = solutionDir.GetFilePath("ReleaseNotes.md"); + +var appVeyorEnv = context.AppVeyor().Environment; + +// Get whether or not this is a local build. +var local = !context.BuildSystem().IsRunningOnAppVeyor; +var isReleaseBuild = !local && appVeyorEnv.Repository.Tag.IsTag; + +// Release notes +var releaseNotes = ParseReleaseNotes(releaseNotesFile); + +// Version +var buildNumber = !isReleaseBuild ? 0 : appVeyorEnv.Build.Number; +var version = releaseNotes.Version.ToString(); +var semVersion = isReleaseBuild ? version : (version + string.Concat("-build-", buildNumber)); + +/////////////////////////////////////////////////////////////////////////////// +// SETUP / TEARDOWN +/////////////////////////////////////////////////////////////////////////////// + +Setup(() => +{ + // Executed BEFORE the first task. + Information("Running tasks..."); + + if (!FileSystem.Exist(testResultsDir)) + { + CreateDirectory(testResultsDir); + } + if (!FileSystem.Exist(nugetPackagingDir)) + { + CreateDirectory(nugetPackagingDir); + } +}); + +Teardown(() => +{ + // Executed AFTER the last task. + Information("Finished running tasks."); +}); + +/////////////////////////////////////////////////////////////////////////////// +// TASK DEFINITIONS +/////////////////////////////////////////////////////////////////////////////// + +Task("Clean") + .Does(() => +{ + // Clean Solution directories + Information("Cleaning {0}", solutionDir); + CleanDirectories(solutionDir + "/packages"); + CleanDirectories(solutionDir + "/**/bin/" + configuration); + CleanDirectories(solutionDir + "/**/obj/" + configuration); + + foreach (var dir in new [] { packagingRoot, testResultsDir }) + { + Information("Cleaning {0}", dir); + CleanDirectory(dir); + } +}); + +Task("Restore") + .IsDependentOn("Clean") + .Does(() => +{ + Information("Restoring {0}", solution); + NuGetRestore(solution); +}); + +Task("AssemblyInfo") + .IsDependentOn("Restore") + .WithCriteria(() => !isReleaseBuild) + .Does(() => +{ + Information("Creating {0} - Version: {1}", solutionInfoCs, version); + CreateAssemblyInfo(solutionInfoCs, new AssemblyInfoSettings { + Product = projectName, + Version = version, + FileVersion = version, + InformationalVersion = semVersion + }); +}); + +Task("Build") + .IsDependentOn("AssemblyInfo") + .Does(() => +{ + Information("Building {0}", solution); + MSBuild(solution, settings => + settings.SetConfiguration(configuration) + ); +}); + +Task("UnitTests") + .IsDependentOn("Build") + .Does(() => +{ + Information("Running Tests in {0}", solution); + + XUnit2( + solutionDir + "/**/bin/" + configuration + "/**/*.Tests*.dll", + new XUnit2Settings { + OutputDirectory = testResultsDir, + HtmlReport = true, + XmlReport = true + } + ); +}); + +Task("CopyNugetPackageFiles") + .IsDependentOn("UnitTests") + .Does(() => +{ + + var baseBuildDir = sourcesDir.Combine(projectName).Combine("bin").Combine(configuration); + + var net45BuildDir = baseBuildDir.Combine("Net45"); + var net45PackageDir = nugetPackagingDir.Combine("lib/net45/"); + + var dirMap = new Dictionary { + { net45BuildDir, net45PackageDir } + }; + + CleanDirectories(dirMap.Values); + + foreach (var dirPair in dirMap) + { + var files = FileSystem.GetDirectory(dirPair.Key) + .GetFiles(projectName + "*", SearchScope.Current) + .Select(x => x.Path); + CopyFiles(files, dirPair.Value); + } + + var packageFiles = new FilePath[] { + licenseFile, + readmeFile, + releaseNotesFile + }; + + CopyFiles(packageFiles, nugetPackagingDir); +}); + +Task("CreateNugetPackage") + .IsDependentOn("CopyNugetPackageFiles") + .Does(() => +{ + NuGetPack( + nuspecFile, + new NuGetPackSettings { + Version = semVersion, + ReleaseNotes = releaseNotes.Notes.ToArray(), + BasePath = nugetPackagingDir, + OutputDirectory = packagingRoot, + Symbols = false, + NoPackageAnalysis = false + } + ); +}); + +/////////////////////////////////////////////////////////////////////////////// +// TASK TARGETS +/////////////////////////////////////////////////////////////////////////////// + +Task("Package") + .IsDependentOn("CreateNugetPackage") + .WithCriteria(() => isReleaseBuild || forcePackage); + +Task("Default") + .IsDependentOn("Package"); + +/////////////////////////////////////////////////////////////////////////////// +// EXECUTION +/////////////////////////////////////////////////////////////////////////////// + +Information("Building {0} [{1}] ({2} - {3}).", solution.GetFilename(), configuration, version, semVersion); + +RunTarget(target); diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..bda15d0 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,47 @@ +Param( + [string] $Target = "Default", + [string] $Configuration = "Debug", + [string] $Verbosity = "normal", + [switch] $ForcePackage +) + +$SelfRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition +$Script = Join-Path $SelfRoot "build.csx" + +$TOOLS_DIR = Join-Path $SelfRoot "tools" +$CAKE_DIR = Join-Path $TOOLS_DIR "Cake" +$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" +$CAKE_EXE = Join-Path $CAKE_DIR "Cake.exe" + +if (!(Test-Path $NUGET_EXE)) { + Throw "Could not find " + $NUGET_EXE +} + +Invoke-Expression "$NUGET_EXE install xunit.runners -OutputDirectory $TOOLS_DIR -ExcludeVersion -Prerelease" +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +Invoke-Expression "$NUGET_EXE install ilmerge -OutputDirectory $TOOLS_DIR -ExcludeVersion -Prerelease" +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +Invoke-Expression "$NUGET_EXE install Cake -OutputDirectory $TOOLS_DIR -ExcludeVersion" +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +if (!(Test-Path $CAKE_EXE)) { + Throw "Could not find " + $CAKE_EXE +} + +$ForcePackageArg = "" +if ($ForcePackage) +{ + $ForcePackageArg = "-forcePackage" +} + +Invoke-Expression "$CAKE_EXE `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $ForcePackageArg" + +exit $LASTEXITCODE diff --git a/meta/Draft.nuspec b/meta/Draft.nuspec new file mode 100644 index 0000000..b6694f4 --- /dev/null +++ b/meta/Draft.nuspec @@ -0,0 +1,20 @@ + + + + Draft + Draft + @version@ + Jordan S. Jones + https://raw.githubusercontent.com/jordansjones/Draft/master/LICENSE.txt + https://github.com/jordansjones/Draft + false + An etcd client library for .Net + @releaseNotes@ + etcd + Copyright 2015 Jordan S. Jones + + + + + + \ No newline at end of file diff --git a/meta/SolutionInfo.cs b/meta/SolutionInfo.cs new file mode 100644 index 0000000..1d1aa56 --- /dev/null +++ b/meta/SolutionInfo.cs @@ -0,0 +1,11 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by Cake. +// +//------------------------------------------------------------------------------ +using System.Reflection; + +[assembly: AssemblyProduct("Draft")] +[assembly: AssemblyVersion("0.1.0")] +[assembly: AssemblyFileVersion("0.1.0")] +[assembly: AssemblyInformationalVersion("0.1.0")] diff --git a/source/Draft/Draft-Net45.csproj b/source/Draft/Draft-Net45.csproj new file mode 100644 index 0000000..c8e3691 --- /dev/null +++ b/source/Draft/Draft-Net45.csproj @@ -0,0 +1,69 @@ + + + + + Debug + AnyCPU + {A4D60581-25BF-4DE9-877C-4179B3F0747C} + Library + Properties + Draft + Draft + v4.5 + 512 + + + true + full + false + bin\Debug\Net45\ + obj\Debug\Net45\ + bin\Debug\Net45\Draft.xml + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\Net45\ + obj\Release\Net45\ + bin\Release\Net45\Draft.xml + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + Properties\SolutionInfo.cs + + + + + + + + \ No newline at end of file diff --git a/source/Draft/Draft-Net45.csproj.DotSettings b/source/Draft/Draft-Net45.csproj.DotSettings new file mode 100644 index 0000000..7a216c5 --- /dev/null +++ b/source/Draft/Draft-Net45.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/source/Draft/HeaderConstants.cs b/source/Draft/HeaderConstants.cs new file mode 100644 index 0000000..af323c9 --- /dev/null +++ b/source/Draft/HeaderConstants.cs @@ -0,0 +1,32 @@ +using System; +using System.Linq; + +namespace Draft +{ + internal static class HeaderConstants + { + /// + /// Appears to be the unique identifier for the cluster + /// + public const string ClusterId = "X-Etcd-Cluster-Id"; + + /// + /// The current etcd index + /// + public const string EtcdIndex = "X-Etcd-Index"; + + /// + /// Similar to the etcd index but is for the underlying raft protocol + /// + public const string RaftIndex = "X-Raft-Index"; + + /// + /// Incrementing integer whenever an etcd master election happens + /// + /// + /// If this number is increasing rapidly, election tuning may be needed + /// + public const string RaftTerm = "X-Raft-Term"; + + } +} diff --git a/source/Draft/Models/ErrorCode.cs b/source/Draft/Models/ErrorCode.cs new file mode 100644 index 0000000..07dd66a --- /dev/null +++ b/source/Draft/Models/ErrorCode.cs @@ -0,0 +1,173 @@ +using System; +using System.Linq; + +namespace Draft.Models +{ + /// + /// Etcd error codes + /// + public enum ErrorCode + { + #region Command related + + /// + /// Key not found + /// + KeyNotFound = 100, + + /// + /// Compare failed + /// + TestFailed = 101, + + /// + /// Not a file + /// + NotFile = 102, + + /// + /// Reached the max number of peers in the cluster + /// + NoMorePeer = 103, + + /// + /// Not a directory + /// + NotDir = 104, + + /// + /// Key already exists + /// + NodeExist = 105, + + /// + /// The prefix of given key is a keyword in etcd + /// + KeyIsPreserved = 106, + + /// + /// Root is read only + /// + RootReadOnly = 107, + + /// + /// Directory not empty + /// + DirNotEmpty = 108, + + /// + /// Peer address has "existed" + /// + ExistingPeerAddress = 109, + + #endregion + + #region Post form related + + /// + /// Value is required in POST form + /// + ValueRequired = 200, + + /// + /// PrevValue is required in POST form + /// + PrevValueRequired = 201, + + /// + /// The given TTL in POST form is not a number + /// + TtlNotANumber = 202, + + /// + /// The given index in POST form is not a number + /// + IndexNotANumber = 203, + + /// + /// Value or TTL is required in POST form + /// + ValueOrTtlRequired = 204, + + /// + /// The given timeout in POST form is not a number + /// + TimeoutNotANumber = 205, + + /// + /// Name is required in POST form + /// + NameRequired = 206, + + /// + /// Index or value is required + /// + IndexOrValueRequired = 207, + + /// + /// Index and value cannot both be specified + /// + IndexValueMutex = 208, + + /// + /// Field is not valid + /// + InvalidField = 209, + + /// + /// Invalid POST form + /// + InvalidForm = 210, + + #endregion + + #region Raft related + + /// + /// Internal RAFT error + /// + RaftInternal = 300, + + /// + /// During leader election + /// + LeaderElect = 301, + + #endregion + + #region Etcd related + + /// + /// Watcher is cleared due to etcd recovery + /// + WatcherCleared = 400, + + /// + /// The event in requested index is outdated and cleared + /// + EventIndexCleared = 401, + + /// + /// Standby Internal Error + /// + StandbyInternal = 402, + + /// + /// Invalid active size + /// + InvalidActiveSize = 403, + + /// + /// Standby remove delay + /// + InvalidRemoveDelay = 404, + + #endregion + + /// + /// Client internal error + /// + ClientInternal = 500 + + } +} diff --git a/source/Draft/Models/Statistics/FollowerCounts.cs b/source/Draft/Models/Statistics/FollowerCounts.cs new file mode 100644 index 0000000..b7e0960 --- /dev/null +++ b/source/Draft/Models/Statistics/FollowerCounts.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; +using System.Runtime.Serialization; + +namespace Draft.Models +{ + /// + /// Follower send counts + /// + [DataContract] + public class FollowerCounts + { + + /// + /// Count of unsuccessful sends + /// + [DataMember(Name = "fail")] + public long Fail { get; private set; } + + /// + /// Count of successful sends + /// + [DataMember(Name = "success")] + public long Success { get; private set; } + + } +} diff --git a/source/Draft/Models/Statistics/FollowerLatency.cs b/source/Draft/Models/Statistics/FollowerLatency.cs new file mode 100644 index 0000000..66ebf07 --- /dev/null +++ b/source/Draft/Models/Statistics/FollowerLatency.cs @@ -0,0 +1,45 @@ +using System; +using System.Linq; +using System.Runtime.Serialization; + +namespace Draft.Models +{ + /// + /// Follower latency statistics + /// + [DataContract] + public class FollowerLatency + { + + /// + /// Follower's average latency + /// + [DataMember(Name = "average")] + public double Average { get; private set; } + + /// + /// Follower's current latency + /// + [DataMember(Name = "current")] + public double Current { get; private set; } + + /// + /// Follower's maximum latency + /// + [DataMember(Name = "maximum")] + public double Maximum { get; private set; } + + /// + /// Follower's minimum latency + /// + [DataMember(Name = "minimum")] + public double Minimum { get; private set; } + + /// + /// Standard Deviation + /// + [DataMember(Name = "standardDeviation")] + public double StandardDeviation { get; private set; } + + } +} diff --git a/source/Draft/Models/Statistics/FollowerStatistics.cs b/source/Draft/Models/Statistics/FollowerStatistics.cs new file mode 100644 index 0000000..bec566c --- /dev/null +++ b/source/Draft/Models/Statistics/FollowerStatistics.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; +using System.Runtime.Serialization; + +namespace Draft.Models +{ + /// + /// Various statistics about a follower in an etcd cluster + /// + [DataContract] + public class FollowerStatistics + { + + /// + /// Follower send counts + /// + [DataMember(Name = "counts")] + public FollowerCounts Counts { get; private set; } + + /// + /// Follower latency statistics + /// + [DataMember(Name = "latency")] + public FollowerLatency Latency { get; private set; } + + } +} diff --git a/source/Draft/Models/Statistics/LeaderInfo.cs b/source/Draft/Models/Statistics/LeaderInfo.cs new file mode 100644 index 0000000..c7f5b6b --- /dev/null +++ b/source/Draft/Models/Statistics/LeaderInfo.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; +using System.Runtime.Serialization; + +namespace Draft.Models +{ + /// + /// Statistical information for a member server's leader + /// + [DataContract] + public class LeaderInfo + { + + /// + /// Id of the current leader + /// + [DataMember(Name = "leader")] + public string Leader { get; private set; } + + /// + /// Time when the leader was started (if available) + /// + [DataMember(Name = "startTime")] + public DateTime? StartTime { get; private set; } + + /// + /// String representation of the amount of time the leader has been leader + /// + [DataMember(Name = "uptime")] + public string Uptime { get; private set; } + + } +} diff --git a/source/Draft/Models/Statistics/LeaderStatistics.cs b/source/Draft/Models/Statistics/LeaderStatistics.cs new file mode 100644 index 0000000..9404f31 --- /dev/null +++ b/source/Draft/Models/Statistics/LeaderStatistics.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Draft.Models +{ + /// + /// Statistics about communication with cluster members + /// + [DataContract] + public class LeaderStatistics + { + /// + /// Id for the currently elected cluster leader + /// + [DataMember(Name = "leader")] + public string Leader { get; private set; } + + /// + /// Map of cluster member id's to cluster member statistics + /// + [DataMember(Name = "followers")] + public IDictionary Followers { get; private set; } + } +} \ No newline at end of file diff --git a/source/Draft/Models/Statistics/ServerStatistics.cs b/source/Draft/Models/Statistics/ServerStatistics.cs new file mode 100644 index 0000000..1c9b947 --- /dev/null +++ b/source/Draft/Models/Statistics/ServerStatistics.cs @@ -0,0 +1,87 @@ +using System; +using System.Linq; +using System.Runtime.Serialization; + +namespace Draft.Models +{ + /// + /// Statistical information on an etcd server member + /// + [DataContract] + public class ServerStatistics + { + + /// + /// The unique identifier for this server member + /// + [DataMember(Name = "id")] + public string Id { get; private set; } + + /// + /// Statistical information for this server member's leader + /// + [DataMember(Name = "leaderInfo")] + public LeaderInfo LeaderInfo { get; private set; } + + /// + /// The name for this server member + /// + [DataMember(Name = "name")] + public string Name { get; private set; } + + /// + /// Number of append requests this server member has processed + /// + [DataMember(Name = "recvAppendRequestCnt")] + public long ReceivedAppendRequestCount { get; private set; } + + /// + /// Number of bytes per second this server member is receiving (follower state only) + /// + [DataMember(Name = "recvBandwidthRate")] + public double? ReceivedBandwidthRate { get; private set; } + + /// + /// Number of requests per second this server member is receiving (follower state only) + /// + [DataMember(Name = "recvPkgRate")] + public double? ReceivedPackageRate { get; private set; } + + /// + /// Numer of requests this server member has sent + /// + [DataMember(Name = "sendAppendRequestCnt")] + public long SendAppendRequestCount { get; private set; } + + /// + /// Number of bytes per second this server member is sending (leader state only) + /// + /// + /// This value is null on single member clusters + /// + [DataMember(Name = "sendBandwidthRate")] + public double? SendBandwidthRate { get; private set; } + + /// + /// Number of requests per second this server member is sending (leader state only) + /// + /// + /// This value is null on single member clusters + /// + [DataMember(Name = "sendPkgRate")] + public double? SendPackageRate { get; private set; } + + /// + /// The time when this server member was started + /// + [DataMember(Name = "startTime")] + public DateTime StartTime { get; private set; } + + /// + /// The cluster state for this server member + /// + [DataMember(Name = "state")] + public StateType State { get; private set; } + + } +} diff --git a/source/Draft/Models/Statistics/StateType.cs b/source/Draft/Models/Statistics/StateType.cs new file mode 100644 index 0000000..0e8ee7e --- /dev/null +++ b/source/Draft/Models/Statistics/StateType.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; + +namespace Draft.Models +{ + /// + /// Represents the leadership type of a member of a cluster + /// + public enum StateType + { + + /// + /// Unable to parse + /// + Unknown, + + /// + /// StateFollower + /// + Follower, + + /// + /// StateCandidate + /// + Candidate, + + /// + /// StateLeader + /// + Leader + + } +} diff --git a/source/Draft/Models/Statistics/StoreStatistics.cs b/source/Draft/Models/Statistics/StoreStatistics.cs new file mode 100644 index 0000000..22b44dd --- /dev/null +++ b/source/Draft/Models/Statistics/StoreStatistics.cs @@ -0,0 +1,99 @@ +using System; +using System.Linq; +using System.Runtime.Serialization; + +namespace Draft.Models +{ + /// + /// Etcd backing store statistics + /// + [DataContract] + public class StoreStatistics + { + + /// + /// Number of failed Compare and Swap requests + /// + [DataMember(Name = "compareAndSwapFail")] + public long CompareAndSwapFail { get; private set; } + + /// + /// Number of successful Compare and Swap requests + /// + [DataMember(Name = "compareAndSwapSuccess")] + public long CompareAndSwapSuccess { get; private set; } + + /// + /// Number of failed Create requests + /// + [DataMember(Name = "createFail")] + public long CreateFail { get; private set; } + + /// + /// Number of successful Create requests + /// + [DataMember(Name = "createSuccess")] + public long CreateSuccess { get; private set; } + + /// + /// Number of failed Delete requests + /// + [DataMember(Name = "deleteFail")] + public long DeleteFail { get; private set; } + + /// + /// Number of successful Delete requests + /// + [DataMember(Name = "deleteSuccess")] + public long DeleteSuccess { get; private set; } + + /// + /// Number of key expirations + /// + [DataMember(Name = "expireCount")] + public long ExpireCount { get; private set; } + + /// + /// Number of failed Get requests + /// + [DataMember(Name = "getsFail")] + public long GetsFail { get; private set; } + + /// + /// Number of successful Get requests + /// + [DataMember(Name = "getsSuccess")] + public long GetsSuccess { get; private set; } + + /// + /// Number of failed Set requests + /// + [DataMember(Name = "setsFail")] + public long SetsFail { get; private set; } + + /// + /// Number of successful Set requests + /// + [DataMember(Name = "setsSuccess")] + public long SetsSuccess { get; private set; } + + /// + /// Number of failed Update requests + /// + [DataMember(Name = "updateFail")] + public long UpdateFail { get; private set; } + + /// + /// Number of successful Update requests + /// + [DataMember(Name = "updateSuccess")] + public long UpdateSuccess { get; private set; } + + /// + /// Number of active watchers + /// + [DataMember(Name = "watchers")] + public long Watchers { get; private set; } + + } +} diff --git a/source/Draft/Properties/AssemblyInfo.cs b/source/Draft/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..69ae21d --- /dev/null +++ b/source/Draft/Properties/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System; +using System.Reflection; + +[assembly: AssemblyTitle("Draft")] +[assembly: AssemblyDescription("An etcd client library for .Net")] diff --git a/source/Draft/packages.Draft-Net45.config b/source/Draft/packages.Draft-Net45.config new file mode 100644 index 0000000..6b8deb9 --- /dev/null +++ b/source/Draft/packages.Draft-Net45.config @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/tests/Draft.Tests/Draft-Net45.Tests.csproj b/tests/Draft.Tests/Draft-Net45.Tests.csproj new file mode 100644 index 0000000..54ecd7c --- /dev/null +++ b/tests/Draft.Tests/Draft-Net45.Tests.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {3F288474-3C7C-4AFA-BE4F-794F32720F9A} + Library + Properties + Draft.Tests + Draft.Tests + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + False + UnitTest + 45450b1e + + + true + full + false + bin\Debug\Net45 + obj\Debug\Net45 + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\Net45 + obj\Release\Net45 + TRACE + prompt + 4 + + + + ..\..\packages\FluentAssertions.3.3.0\lib\net45\FluentAssertions.dll + + + ..\..\packages\FluentAssertions.3.3.0\lib\net45\FluentAssertions.Core.dll + + + + + + ..\..\packages\xunit.abstractions.2.0.0-rc2-build2857\lib\net35\xunit.abstractions.dll + + + ..\..\packages\xunit.assert.2.0.0-rc2-build2857\lib\portable-net45+aspnetcore50+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.assert.dll + + + ..\..\packages\xunit.extensibility.core.2.0.0-rc2-build2857\lib\portable-net45+aspnetcore50+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core.dll + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/tests/Draft.Tests/Properties/AssemblyInfo.cs b/tests/Draft.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a84014b --- /dev/null +++ b/tests/Draft.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,9 @@ +using System.Reflection; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Draft.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyProduct("Draft.Tests")] +[assembly: AssemblyCopyright("Copyright © 2015")] diff --git a/tests/Draft.Tests/packages.Draft-Net45.Tests.config b/tests/Draft.Tests/packages.Draft-Net45.Tests.config new file mode 100644 index 0000000..9fccf98 --- /dev/null +++ b/tests/Draft.Tests/packages.Draft-Net45.Tests.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..df9d16a --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,5 @@ +# Ignore everything +* +# But not these files +!.gitignore +!nuget.exe \ No newline at end of file diff --git a/tools/nuget.exe b/tools/nuget.exe new file mode 100644 index 0000000..8dd7e45 Binary files /dev/null and b/tools/nuget.exe differ