PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/msvs/RedisWAInst/src/RedisInstWA/Program.cs

https://github.com/jope1982/redis
C# | 507 lines | 454 code | 14 blank | 39 comment | 73 complexity | bcec78c34cea3410432cd8b550d549f5 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. #region Copyright Notice
  2. /*
  3. Copyright © Microsoft Open Technologies, Inc.
  4. All Rights Reserved
  5. Apache 2.0 License
  6. Licensed under the Apache License, Version 2.0 (the "License");
  7. you may not use this file except in compliance with the License.
  8. You may obtain a copy of the License at
  9. http://www.apache.org/licenses/LICENSE-2.0
  10. Unless required by applicable law or agreed to in writing, software
  11. distributed under the License is distributed on an "AS IS" BASIS,
  12. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. See the License for the specific language governing permissions and
  14. limitations under the License.
  15. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
  16. */
  17. #endregion
  18. using Shell32;
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Diagnostics;
  22. using System.IO;
  23. using System.Linq;
  24. using System.Reflection;
  25. using System.Security.Principal;
  26. using System.Text;
  27. namespace RedisInstWA
  28. {
  29. class Program
  30. {
  31. // values from input args
  32. static string GitUrl;
  33. static string LocalExePath;
  34. static bool IsLocal;
  35. static string ConfigFolderPath;
  36. static string RedisConfPath;
  37. static bool IsEmul;
  38. static string Domain;
  39. static string Subscription;
  40. static string passThough;
  41. // Values from environment or configuration
  42. static string WorkPath;
  43. static string RedisInstExePath;
  44. static string PubSettingFilePath;
  45. static List<RedisInstance> Instances;
  46. const string WorkDir = "RedisInstWork";
  47. const string BinariesSubDir = "release";
  48. /// <summary>
  49. /// Prepare and initiate install of Redis to Azure
  50. /// </summary>
  51. /// <param name="args"></param>
  52. static void Main(string[] args)
  53. {
  54. if (!IsElevated)
  55. {
  56. Console.WriteLine("RedisInstWA must be run as an Administrator");
  57. Environment.Exit(1);
  58. }
  59. if (args == null || args.Count() == 0)
  60. {
  61. Console.WriteLine("No parameters were provided\r\n");
  62. Usage();
  63. Environment.Exit(1);
  64. }
  65. else
  66. {
  67. if (!ValidateArgs(args))
  68. {
  69. Console.WriteLine("Usage:");
  70. Usage();
  71. Environment.Exit(1);
  72. }
  73. // create work directory for copying files
  74. string curpath = Directory.GetCurrentDirectory();
  75. WorkPath = Path.Combine(curpath, WorkDir);
  76. if (!FolderHelper.MakeFolder(WorkPath))
  77. {
  78. Console.WriteLine("Failed to make folder for staging the installation");
  79. Environment.Exit(2);
  80. }
  81. RedisInstExePath = Assembly.GetExecutingAssembly().Location;
  82. RedisInstExePath = Path.GetDirectoryName(RedisInstExePath);
  83. // Download and extract exe from zip file or copy local exe
  84. if (!PrepareBinaries(WorkPath))
  85. {
  86. Console.WriteLine("Failed to copy redis executable files to staging directory");
  87. Environment.Exit(2);
  88. }
  89. // Parse the ServiceDefinition to extract roles and ports
  90. if (!ParseConfiguration())
  91. {
  92. Console.WriteLine("Failed to parse the ServiceDefinition file");
  93. Environment.Exit(3);
  94. }
  95. // Prepare instances and xml file for Inst4WA
  96. if (!PrepareInstaller())
  97. {
  98. Console.WriteLine("Failed to create the XML Configuration file for the installer");
  99. Environment.Exit(4);
  100. }
  101. // run Inst4WA to do install
  102. if (!RunInstaller())
  103. {
  104. Console.WriteLine("Failed to execute Inst4WA process to install Redis");
  105. Environment.Exit(5);
  106. }
  107. }
  108. Environment.Exit(0);
  109. }
  110. /// <summary>
  111. /// Validate input parameters
  112. /// </summary>
  113. /// <param name="args"></param>
  114. /// <returns></returns>
  115. private static bool ValidateArgs(string[] args)
  116. {
  117. bool isSource = false;
  118. bool isConfig = false;
  119. bool isRedisConf = false;
  120. bool isEmuOrAzure = false;
  121. passThough = "";
  122. for (int i = 0; i < args.Length; i++)
  123. {
  124. if (args[i].StartsWith("-So", StringComparison.InvariantCultureIgnoreCase))
  125. {
  126. if (i == args.Length - 1)
  127. {
  128. Console.WriteLine("Missing argument after: " + args[i]);
  129. return false;
  130. }
  131. i++;
  132. if (args[i].StartsWith("http", StringComparison.InvariantCultureIgnoreCase))
  133. {
  134. if (Uri.IsWellFormedUriString(args[i], UriKind.Absolute) == false)
  135. {
  136. Console.WriteLine("Source URL format is invalid: " + args[i]);
  137. return false;
  138. }
  139. GitUrl = args[i];
  140. IsLocal = false;
  141. }
  142. else if (!Directory.Exists(args[i]))
  143. {
  144. Console.WriteLine("Source path does not exist: " + args[i]);
  145. return false;
  146. }
  147. else
  148. {
  149. string sourcePath = Path.GetFullPath(args[i]);
  150. string[] files = Directory.GetFiles(sourcePath, "redis-server.exe");
  151. if (files == null || files.Length == 0)
  152. {
  153. Console.WriteLine("Source path does not contain redis-server.exe: " + args[i]);
  154. return false;
  155. }
  156. IsLocal = true;
  157. LocalExePath = sourcePath;
  158. }
  159. isSource = true;
  160. }
  161. else if (args[i].StartsWith("-Co", StringComparison.InvariantCultureIgnoreCase))
  162. {
  163. if (i == args.Length - 1)
  164. {
  165. Console.WriteLine("Missing argument after: " + args[i]);
  166. return false;
  167. }
  168. i++;
  169. if (!Directory.Exists(args[i]))
  170. {
  171. Console.WriteLine("Configuration path does not exist: " + args[i]);
  172. return false;
  173. }
  174. else
  175. {
  176. string configPath = Path.GetFullPath(args[i]);
  177. string[] files = Directory.GetFiles(configPath, "ServiceDefinition.csdef");
  178. if (files == null || files.Length == 0)
  179. {
  180. Console.WriteLine("Configuration path does not contain ServiceDefinition.csdef: " + args[i]);
  181. return false;
  182. }
  183. files = Directory.GetFiles(configPath, "ServiceConfiguration.Local.cscfg");
  184. if (files == null || files.Length == 0)
  185. {
  186. Console.WriteLine("Configuration path does not contain ServiceConfiguration.Local.cscfg: " + args[i]);
  187. return false;
  188. }
  189. files = Directory.GetFiles(configPath, "ServiceConfiguration.Cloud.cscfg");
  190. if (files == null || files.Length == 0)
  191. {
  192. Console.WriteLine("Configuration path does not contain ServiceConfiguration.Cloud.cscfg: " + args[i]);
  193. return false;
  194. }
  195. files = Directory.GetFiles(configPath, "*.publishsettings");
  196. if (files != null && files.Length > 0)
  197. {
  198. // found at least one publishsettings file. Choose first one.
  199. PubSettingFilePath = files[0];
  200. }
  201. else
  202. {
  203. PubSettingFilePath = null;
  204. }
  205. ConfigFolderPath = configPath;
  206. isConfig = true;
  207. }
  208. }
  209. else if (args[i].StartsWith("-Re", StringComparison.InvariantCultureIgnoreCase))
  210. {
  211. if (i == args.Length - 1)
  212. {
  213. Console.WriteLine("Missing argument after: " + args[i]);
  214. return false;
  215. }
  216. i++;
  217. if (!Directory.Exists(args[i]))
  218. {
  219. Console.WriteLine("RedisConf path does not exist: " + args[i]);
  220. return false;
  221. }
  222. else
  223. {
  224. string redisconfPath = Path.GetFullPath(args[i]);
  225. string[] files = Directory.GetFiles(redisconfPath, "redis.conf");
  226. if (files == null || files.Length == 0)
  227. {
  228. Console.WriteLine("RedisConf path does not contain redis.conf: " + args[i]);
  229. return false;
  230. }
  231. RedisConfPath = redisconfPath;
  232. isRedisConf = true;
  233. }
  234. }
  235. else if (args[i].StartsWith("-Em", StringComparison.InvariantCultureIgnoreCase))
  236. {
  237. if (isEmuOrAzure && !IsEmul)
  238. {
  239. Console.WriteLine("Both -Emul and -Domain or -Subscription may not be specified ");
  240. return false;
  241. }
  242. IsEmul = true;
  243. isEmuOrAzure = true;
  244. }
  245. else if (args[i].StartsWith("-Do", StringComparison.InvariantCultureIgnoreCase))
  246. {
  247. if (i == args.Length - 1)
  248. {
  249. Console.WriteLine("Missing argument after: " + args[i]);
  250. return false;
  251. }
  252. i++;
  253. if (isEmuOrAzure && IsEmul)
  254. {
  255. Console.WriteLine("Both -Emul and -Domain may not be specified ");
  256. return false;
  257. }
  258. isEmuOrAzure = true;
  259. IsEmul = false;
  260. Domain = args[i];
  261. }
  262. else if (args[i].StartsWith("-Su", StringComparison.InvariantCultureIgnoreCase))
  263. {
  264. if (i == args.Length - 1)
  265. {
  266. Console.WriteLine("Missing argument after: " + args[i]);
  267. return false;
  268. }
  269. i++;
  270. if (isEmuOrAzure && IsEmul)
  271. {
  272. Console.WriteLine("Both -Emul and -Subscription may not be specified ");
  273. return false;
  274. }
  275. isEmuOrAzure = true;
  276. IsEmul = false;
  277. Subscription = args[i];
  278. }
  279. else if (args[i].StartsWith("--Pa", StringComparison.InvariantCultureIgnoreCase))
  280. {
  281. // Following parameters are passed on to Inst4WA
  282. passThough = " ";
  283. i++;
  284. while (i < args.Length)
  285. {
  286. // put remaining args into a string with space spearator
  287. // If arg doesn't start with -, enclose with quotes
  288. if (args[i].StartsWith("-"))
  289. passThough += args[i] + " ";
  290. else
  291. passThough += @"""" + args[i] + @""" ";
  292. i++;
  293. }
  294. break;
  295. }
  296. else
  297. {
  298. Console.WriteLine("Unknown option specified " + args[i]);
  299. return false;
  300. }
  301. }
  302. if (!isSource)
  303. {
  304. Console.WriteLine("Missing -Source argument");
  305. return false;
  306. }
  307. if (!isConfig)
  308. {
  309. Console.WriteLine("Missing -Configuration argument");
  310. return false;
  311. }
  312. if (!isRedisConf)
  313. {
  314. Console.WriteLine("Missing -RedisConf argument");
  315. return false;
  316. }
  317. if (!isEmuOrAzure)
  318. {
  319. Console.WriteLine("Missing -Emul or -Domain and -Subscription argument");
  320. return false;
  321. }
  322. if (!IsEmul && String.IsNullOrWhiteSpace(Domain))
  323. {
  324. Console.WriteLine("Missing -Domain argument");
  325. return false;
  326. }
  327. if (!IsEmul && String.IsNullOrWhiteSpace(Subscription))
  328. {
  329. Console.WriteLine("Missing -Subscription argument");
  330. return false;
  331. }
  332. return true;
  333. }
  334. /// <summary>
  335. /// Place installable files in folders
  336. /// </summary>
  337. /// <param name="workPath"></param>
  338. /// <returns></returns>
  339. private static bool PrepareBinaries(string workPath)
  340. {
  341. string WorkBinariesPath = Path.Combine(workPath, BinariesSubDir);
  342. bool isCopied = false;
  343. if (!IsLocal)
  344. {
  345. ExtractZip exz = new ExtractZip();
  346. int rc = exz.DownloadAndExtract(GitUrl, workPath, BinariesSubDir);
  347. isCopied = rc == 0;
  348. }
  349. else
  350. {
  351. if (!FolderHelper.MakeFolder(workPath))
  352. return false;
  353. if (LocalExePath != WorkBinariesPath)
  354. {
  355. if (!FolderHelper.MakeFolderReset(WorkBinariesPath))
  356. return false;
  357. Shell sh = new Shell();
  358. Folder outdir = sh.NameSpace(WorkBinariesPath);
  359. IEnumerable<string> subfiles = Directory.EnumerateFiles(LocalExePath, "*.exe");
  360. foreach (string file in subfiles)
  361. {
  362. string filepath = Path.Combine(LocalExePath, file);
  363. outdir.CopyHere(filepath, 0); // 4 for no progress dialog, 16 for Yes to all
  364. }
  365. subfiles = Directory.EnumerateFiles(LocalExePath, "*.pdb");
  366. foreach (string file in subfiles)
  367. {
  368. string filepath = Path.Combine(LocalExePath, file);
  369. outdir.CopyHere(filepath, 0); // 4 for no progress dialog, 16 for Yes to all
  370. }
  371. }
  372. // return true if redis-server exists
  373. isCopied = File.Exists(Path.Combine(WorkBinariesPath, @"redis-server.exe"));
  374. }
  375. if (isCopied)
  376. {
  377. // copy redis.conf file to workPath
  378. string redisconf = File.ReadAllText(Path.Combine(RedisConfPath, @"redis.conf"));
  379. File.WriteAllText(Path.Combine(workPath, @"redis.conf"), redisconf);
  380. }
  381. return isCopied;
  382. }
  383. /// <summary>
  384. /// Parse csdef file to extract installation properties
  385. /// </summary>
  386. /// <returns></returns>
  387. private static bool ParseConfiguration()
  388. {
  389. string pathCsdef = Path.Combine(ConfigFolderPath, "ServiceDefinition.csdef");
  390. Instances = ParseConfig.Parse(pathCsdef);
  391. return Instances.Count > 0;
  392. }
  393. /// <summary>
  394. /// Prepare XML file used as input to Inst4WA
  395. /// </summary>
  396. /// <returns></returns>
  397. private static bool PrepareInstaller()
  398. {
  399. return InstWaXml.CreateInstXml(IsEmul,
  400. ConfigFolderPath,
  401. WorkPath,
  402. WorkPath,
  403. RedisInstExePath,
  404. BinariesSubDir,
  405. Domain,
  406. Subscription,
  407. PubSettingFilePath,
  408. Instances);
  409. }
  410. private static bool RunInstaller()
  411. {
  412. string args = "-XmlConfigPath " + @"""" + Path.Combine(WorkPath, "XmlConfig.xml") + @"""";
  413. if (!IsEmul)
  414. {
  415. // append DomainName and Subscription
  416. args = args + " -DomainName " + Domain + " -Subscription " + Subscription;
  417. }
  418. // append passthrough args
  419. args = args + passThough;
  420. // create process info to run inst4wa
  421. Process p = new Process();
  422. p.StartInfo.FileName = Path.Combine(RedisInstExePath, @"Inst4WA\Inst4WA.exe");
  423. p.StartInfo.Arguments = args;
  424. p.StartInfo.UseShellExecute = false;
  425. p.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory();
  426. Console.WriteLine("Launching " + p.StartInfo.FileName + " " + p.StartInfo.Arguments);
  427. p.Start();
  428. p.WaitForExit();
  429. p.Close();
  430. return true;
  431. }
  432. private static bool IsElevated
  433. {
  434. get
  435. {
  436. return new WindowsPrincipal
  437. (WindowsIdentity.GetCurrent()).IsInRole
  438. (WindowsBuiltInRole.Administrator);
  439. }
  440. }
  441. private static void Usage()
  442. {
  443. string msg = "RedisInstWA installs Redis to Windows Azure.\r\n" +
  444. "RedisInstWA -Source <Github URL to ZIP> | <path to redis-server.exe>\r\n" +
  445. " -Config <path to install configuration>\r\n" +
  446. " -RedisConf <path to redis.conf>\r\n" +
  447. " -Emu | -Domain <Azure domain> -Subscription <Azure subscription>\r\n" +
  448. " [--Pass <other parameters>]\r\n" +
  449. "Where:\r\n" +
  450. "-Source <Github URL to ZIP>: URL to download redis. For example:\r\n" +
  451. " 'https://github.com/MSOpenTech/redis/archive/2.4.zip'\r\n" +
  452. "-Source <path to redis-server.exe>: local path to folder with redis exe files\r\n" +
  453. "-Config <path to install configuration>: local path to folder with .csdef file\r\n" +
  454. "-RedisConf <path to redis.conf>: local path to folder with redis.conf file\r\n" +
  455. "-Emu: Use this if deploying to emulator\r\n" +
  456. "-Domain <Azure domain>: Name to be used to create the hosted service in Azure\r\n" +
  457. "-Subscription <Azure subscription>: Windows Azure subscription name (not ID)\r\n" +
  458. "--Pass <other parameters>: Optional. Parameters after --Pass are for Inst4WA\r\n" +
  459. " This can be used to override settings such as Region\r\n" +
  460. "\r\n" +
  461. "Notes:\r\n" +
  462. "Only the first 2 characters of each option are needed (-So is same as -Source)\r\n\r\n" +
  463. "The installer must be run with elevated Administrator privileges\r\n\r\n" +
  464. "Specify one redis.conf for all instances.\r\n" +
  465. " For each instance the port number is appended.\r\n" +
  466. " For each slave, the master endpoint is appended\r\n" +
  467. "\r\n";
  468. Console.WriteLine(msg);
  469. }
  470. }
  471. }