├── tools
└── NuGet.exe
├── .gitignore
├── WazMonkey
├── packages.config
├── Properties
│ └── AssemblyInfo.cs
├── WazMonkey.csproj
└── Program.cs
├── WazMonkey.sln
└── README.md
/tools/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarx/WazMonkey/HEAD/tools/NuGet.exe
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | obj
2 | bin
3 | *.user
4 | *.suo
5 | *.cache
6 | *.nupkg
7 | packages/
8 |
--------------------------------------------------------------------------------
/WazMonkey/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/WazMonkey.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WazMonkey", "WazMonkey\WazMonkey.csproj", "{64597F68-E7A2-4DE1-A89C-222DB35DC2BF}"
5 | EndProject
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{7C06F84E-6400-4C19-AF5A-541E548ECFC0}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {64597F68-E7A2-4DE1-A89C-222DB35DC2BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {64597F68-E7A2-4DE1-A89C-222DB35DC2BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {64597F68-E7A2-4DE1-A89C-222DB35DC2BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {64597F68-E7A2-4DE1-A89C-222DB35DC2BF}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | WazMonkey is a simple tool for testing resilience of Windows Azure cloud services.
2 |
3 | It's modeled after [Netflix's Chaos Monkey](http://techblog.netflix.com/2012/07/chaos-monkey-released-into-wild.html).
4 | WazMonkey chooses a role instance from your service at random and reboots it.
5 |
6 | ## Usage:
7 |
8 | WazMonkey v0.2
9 | Copyright (C) 2012 Steve Marx
10 | Usage: WazMonkey -p foo.publishSettings -n myservice -s production
11 |
12 | -p, --publishSettings .publishSettings file - specify either this or a
13 | .pfx file
14 |
15 | --pfx .pfx certificate file - specify either this or a
16 | .publishSettings file
17 |
18 | --subscriptionId subscriptionId to use, defaults to first
19 | subscription found in the .publishSettings file
20 |
21 | -n, --serviceName Required. Name of the cloud service
22 |
23 | -s, --slot Required. The slot ("production" or "staging")
24 |
25 | --reimage Reimage the instance (instead of reboot, the
26 | default)
27 |
28 | --help Display this help screen.
29 |
--------------------------------------------------------------------------------
/WazMonkey/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("WazMonkey")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("WazMonkey")]
13 | [assembly: AssemblyCopyright("Copyright © 2012")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("03ac350e-8227-472b-bf6b-ca08d451fcc5")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/WazMonkey/WazMonkey.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {64597F68-E7A2-4DE1-A89C-222DB35DC2BF}
8 | Exe
9 | Properties
10 | WazMonkey
11 | WazMonkey
12 | v4.0
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 | ..\packages\CommandLineParser.1.9.3.19\lib\CommandLine.dll
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | $(SolutionDir)Tools\nuget install $(ProjectDir)packages.config -o $(SolutionDir)Packages
56 |
57 |
64 |
--------------------------------------------------------------------------------
/WazMonkey/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using CommandLine;
6 | using CommandLine.Text;
7 | using System.Xml.Linq;
8 | using System.Security.Cryptography.X509Certificates;
9 | using System.Net;
10 | using System.Globalization;
11 |
12 | namespace WazMonkey
13 | {
14 | class Options : CommandLineOptionsBase
15 | {
16 | [Option("p", "publishSettings", Required = false, HelpText = ".publishSettings file - specify either this or a .pfx file")]
17 | public string PublishSettingsFile { get; set; }
18 |
19 | [Option(null, "pfx", Required = false, HelpText = ".pfx certificate file - specify either this or a .publishSettings file")]
20 | public string PfxFile { get; set; }
21 |
22 | [Option(null, "subscriptionId", Required = false, HelpText = "subscriptionId to use, defaults to first subscription found in the .publishSettings file")]
23 | public string SubscriptionId { get; set; }
24 |
25 | [Option("n", "serviceName", Required = true, HelpText = "Name of the cloud service")]
26 | public string ServiceName { get; set; }
27 |
28 | [Option("s", "slot", Required = true, HelpText = @"The slot (""production"" or ""staging"")")]
29 | public string Slot { get; set; }
30 |
31 | [Option(null, "reimage", Required = false, HelpText = "Reimage the instance (instead of reboot, the default)")]
32 | public bool Reimage { get; set; }
33 |
34 | [HelpOption(HelpText = "Display this help screen.")]
35 | public string GetUsage()
36 | {
37 | var help = new HelpText
38 | {
39 | Heading = new HeadingInfo("WazMonkey", "v0.2"),
40 | Copyright = new CopyrightInfo("Steve Marx", 2012),
41 | AdditionalNewLineAfterOption = true,
42 | AddDashesToOption = true
43 | };
44 | help.AddPreOptionsLine("Usage: WazMonkey -p foo.publishSettings -n myservice -s production");
45 | help.AddOptions(this);
46 | return help;
47 | }
48 | }
49 |
50 | class Program
51 | {
52 | static void Main(string[] args)
53 | {
54 | var options = new Options();
55 | if (CommandLineParser.Default.ParseArguments(args, options))
56 | {
57 | if (options.PublishSettingsFile == null && options.PfxFile == null)
58 | {
59 | Console.WriteLine("Required: one of --publishSettings or --pfx");
60 | Console.WriteLine(options.GetUsage());
61 | Environment.Exit(1);
62 | }
63 | if (options.PublishSettingsFile != null && options.PfxFile != null)
64 | {
65 | Console.WriteLine("Incompatible arguments: --publishSettings and --pfx");
66 | Console.WriteLine(options.GetUsage());
67 | Environment.Exit(1);
68 | }
69 | if (options.PublishSettingsFile == null && options.SubscriptionId == null)
70 | {
71 | Console.WriteLine("--subscriptionId must be provided if no .publishSettings file is being used");
72 | Console.WriteLine(options.GetUsage());
73 | Environment.Exit(1);
74 | }
75 | if (options.Slot != "production" && options.Slot != "staging")
76 | {
77 | Console.WriteLine(@"Slot must be one of ""production"" or ""staging""");
78 | Console.WriteLine(options.GetUsage());
79 | Environment.Exit(1);
80 | }
81 | }
82 | else
83 | {
84 | Environment.Exit(1);
85 | }
86 |
87 | X509Certificate2 cert;
88 | string subscriptionId = options.SubscriptionId;
89 |
90 | if (options.PublishSettingsFile != null)
91 | {
92 | var profile = XDocument.Load(options.PublishSettingsFile);
93 | if (subscriptionId == null)
94 | {
95 | subscriptionId = profile.Descendants("Subscription").First().Attribute("Id").Value;
96 | }
97 | cert = new X509Certificate2(Convert.FromBase64String(
98 | profile.Descendants("PublishProfile").Single()
99 | .Attribute("ManagementCertificate").Value));
100 | }
101 | else
102 | {
103 | cert = new X509Certificate2(options.PfxFile);
104 | }
105 |
106 | var baseAddress = string.Format("https://management.core.windows.net/{0}/services/hostedservices/{1}/deploymentslots/{2}", subscriptionId, options.ServiceName, options.Slot);
107 |
108 | // request details on specified deployment
109 | var req = (HttpWebRequest)WebRequest.Create(baseAddress);
110 | req.Headers["x-ms-version"] = "2012-03-01";
111 | req.ClientCertificates.Add(cert);
112 |
113 | // find all instances
114 | XNamespace xmlns = "http://schemas.microsoft.com/windowsazure";
115 | try
116 | {
117 | var responseStream = req.GetResponse().GetResponseStream();
118 | }
119 | catch (WebException e)
120 | {
121 | if (((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.NotFound)
122 | {
123 | var titleCaseSlot = new CultureInfo("en-us", false).TextInfo.ToTitleCase(options.Slot);
124 | Console.WriteLine("Error: {0} deployment for service {1} not found, or the credentials were invalid.", titleCaseSlot, options.ServiceName);
125 | Environment.Exit(1);
126 | }
127 | else
128 | {
129 | throw;
130 | }
131 | }
132 | var instances = XDocument.Load(req.GetResponse().GetResponseStream()).Descendants(xmlns + "InstanceName").Select(n => n.Value).ToArray();
133 |
134 | // choose one at random
135 | var instance = instances[new Random().Next(instances.Length)];
136 | Console.WriteLine("{0} {1}.", options.Reimage ? "Reimaging" : "Rebooting", instance);
137 |
138 | // reboot it
139 | req = (HttpWebRequest)WebRequest.Create(string.Format("{0}/roleinstances/{1}?comp={2}", baseAddress, instance, options.Reimage ? "reimage" : "reboot"));
140 | req.Method = "POST";
141 | req.ContentLength = 0;
142 | req.Headers["x-ms-version"] = "2012-03-01";
143 | req.ClientCertificates.Add(cert);
144 |
145 | // make sure the response was "accepted"
146 | var response = (HttpWebResponse)req.GetResponse();
147 | if (response.StatusCode != HttpStatusCode.Accepted)
148 | {
149 | Console.WriteLine("Got unexpected status code: {0}", response.StatusCode);
150 | }
151 | }
152 | }
153 | }
154 |
--------------------------------------------------------------------------------