Permalink
Browse files

Merge pull request #112 from tedglaza-msft/DontPackageLogs

Fix #60 by ignoring iisnode *.logs directories when publishing
  • Loading branch information...
2 parents 016ad2c + b76f76e commit a82edc01d7ea94876093059e7717e5ddc1f2c045 tedglaza-msft committed Jan 14, 2012
@@ -10,19 +10,19 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-// ----------------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Packaging;
-using System.Linq;
-using AzureDeploymentCmdlets.Cmdlet;
-using AzureDeploymentCmdlets.Node.Cmdlet;
-using AzureDeploymentCmdlets.WAPPSCmdlet;
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Packaging;
+using System.Linq;
+using System.ServiceModel;
+using AzureDeploymentCmdlets.Cmdlet;
+using AzureDeploymentCmdlets.Model;
+using AzureDeploymentCmdlets.Node.Cmdlet;
+using AzureDeploymentCmdlets.WAPPSCmdlet;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using AzureDeploymentCmdlets.Model;
-using System.ServiceModel;
namespace AzureDeploymentCmdlets.Test.Tests.Cmdlet
{
@@ -474,6 +474,85 @@ public void PublishAzureServiceCreateDeploymentForExistingService()
Assert.IsTrue(createdOrUpdatedDeployment);
Assert.AreEqual<string>(serviceName, service.ServiceName);
}
+ }
+
+ /// <summary>
+ /// Ensure that any iisnode logs are removed prior to packaging the
+ /// service.
+ ///</summary>
+ [TestMethod]
+ public void PublishAzureServiceRemovesNodeLogs()
+ {
+ // Create a temp directory that we'll use to "publish" our service
+ using (FileSystemHelper files = new FileSystemHelper(this) { EnableMonitoring = true })
+ {
+ // Import our default publish settings
+ files.CreateAzureSdkDirectoryAndImportPublishSettings();
+
+ // Create a new channel to mock the calls to Azure and
+ // determine all of the results that we'll need.
+ SimpleServiceManagement channel = new SimpleServiceManagement();
+
+ // Create a new service that we're going to publish
+ string serviceName = "TEST_SERVICE_NAME";
+ NewAzureServiceCommand newService = new NewAzureServiceCommand();
+ newService.NewAzureServiceProcess(files.RootPath, serviceName);
+ string servicePath = files.CreateDirectory(serviceName);
+
+ // Add a web role
+ AddAzureNodeWebRoleCommand newWebRole = new AddAzureNodeWebRoleCommand();
+ string webRoleName = "NODE_WEB_ROLE";
+ newWebRole.AddAzureNodeWebRoleProcess(webRoleName, 2, servicePath);
+ string webRolePath = Path.Combine(servicePath, webRoleName);
+
+ // Add a worker role
+ AddAzureNodeWorkerRoleCommand newWorkerRole = new AddAzureNodeWorkerRoleCommand();
+ string workerRoleName = "NODE_WORKER_ROLE";
+ newWorkerRole.AddAzureNodeWorkerRoleProcess(workerRoleName, 2, servicePath);
+ string workerRolePath = Path.Combine(servicePath, workerRoleName);
+
+ // Add second web and worker roles that we won't add log
+ // entries to
+ new AddAzureNodeWebRoleCommand()
+ .AddAzureNodeWebRoleProcess("SECOND_WEB_ROLE", 2, servicePath);
+ new AddAzureNodeWorkerRoleCommand()
+ .AddAzureNodeWorkerRoleProcess("SECOND_WORKER_ROLE", 2, servicePath);
+
+ // Add fake logs directories for server.js
+ string logName = "server.js.logs";
+ string logPath = Path.Combine(webRolePath, logName);
+ Directory.CreateDirectory(logPath);
+ File.WriteAllText(Path.Combine(logPath, "0.txt"), "secret web role debug details were logged here");
+ logPath = Path.Combine(Path.Combine(workerRolePath, "NestedDirectory"), logName);
+ Directory.CreateDirectory(logPath);
+ File.WriteAllText(Path.Combine(logPath, "0.txt"), "secret worker role debug details were logged here");
+
+ // Get the publishing process started by creating the package
+ PublishAzureServiceCommand publishService = new PublishAzureServiceCommand(channel);
+ publishService.InitializeSettingsAndCreatePackage(servicePath);
+
+ // Rip open the package and make sure we can't find the log
+ string packagePath = Path.Combine(servicePath, "cloud_package.cspkg");
+ using (Package package = Package.Open(packagePath))
+ {
+ // Make sure the web role and worker role packages don't
+ // have any files with server.js.logs in the name
+ Action<string> validateRole = roleName =>
+ {
+ PackagePart rolePart = package.GetParts().Where(p => p.Uri.ToString().Contains(roleName)).First();
+ using (Package rolePackage = Package.Open(rolePart.GetStream()))
+ {
+ Assert.IsFalse(
+ rolePackage.GetParts().Any(p => p.Uri.ToString().Contains(logName)),
+ "Found {0} part in {1} package!",
+ logName,
+ roleName);
+ }
+ };
+ validateRole(webRoleName);
+ validateRole(workerRoleName);
+ }
+ }
}
}
}
@@ -12,74 +12,167 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Security.Permissions;
-using AzureDeploymentCmdlets.Model;
-using AzureDeploymentCmdlets.Properties;
-using AzureDeploymentCmdlets.ServiceDefinitionSchema;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Security.Permissions;
+using AzureDeploymentCmdlets.Model;
+using AzureDeploymentCmdlets.Properties;
+using AzureDeploymentCmdlets.ServiceDefinitionSchema;
using AzureDeploymentCmdlets.Utilities;
namespace AzureDeploymentCmdlets.AzureTools
{
+ /// <summary>
+ /// Package services for deployment to Azure.
+ /// </summary>
public class CsPack : AzureTool
{
- string cspackPath;
-
- public CsPack()
- {
- cspackPath = Path.Combine(base.AzureSdkBinDirectory, Resources.CsPackExe);
- }
-
+ /// <summary>
+ /// Create a .cspkg package by calling the CsPack.exe tool.
+ /// </summary>
+ /// <param name="definition">Service definition</param>
+ /// <param name="rootPath">Path to the service definition</param>
+ /// <param name="type">Deployment type</param>
+ /// <param name="standardOutput">Standard output</param>
+ /// <param name="standardError">Standard error</param>
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
public void CreatePackage(ServiceDefinition definition, string rootPath, DevEnv type, out string standardOutput, out string standardError)
- {
- string arguments;
-
- arguments = ConstructArgs(definition, rootPath, type);
- Execute(arguments, out standardOutput, out standardError);
- }
-
- private string ConstructArgs(ServiceDefinition serviceDefinition, string rootPath, DevEnv type)
- {
- string arguments;
- string rolesArg = "";
- string sitesArg = "";
-
- if (serviceDefinition == null) throw new ArgumentNullException("serviceDefinition", string.Format(Resources.InvalidOrEmptyArgumentMessage, "Service definition"));
- if (string.IsNullOrEmpty(rootPath) || System.IO.File.Exists(rootPath)) throw new ArgumentException(Resources.InvalidRootNameMessage, "rootPath");
-
- if (serviceDefinition.WebRole != null)
- {
- foreach (WebRole webRole in serviceDefinition.WebRole)
- {
- rolesArg += string.Format(Resources.RoleArgTemplate, webRole.name, rootPath);
-
- foreach (Site site in webRole.Sites.Site)
- {
- sitesArg += string.Format(Resources.SitesArgTemplate, webRole.name, site.name, rootPath);
- }
- }
- }
-
- if (serviceDefinition.WorkerRole != null)
- {
- foreach (WorkerRole workerRole in serviceDefinition.WorkerRole)
- {
- rolesArg += string.Format(Resources.RoleArgTemplate, workerRole.name, rootPath);
- }
+ {
+ if (definition == null)
+ {
+ throw new ArgumentNullException(
+ "definition",
+ string.Format(Resources.InvalidOrEmptyArgumentMessage, "Service definition"));
+ }
+ if (string.IsNullOrEmpty(rootPath) || File.Exists(rootPath))
+ {
+ throw new ArgumentException(Resources.InvalidRootNameMessage, "rootPath");
+ }
+
+ // Track the directories that are created by GetOrCreateCleanPath
+ // to avoid publishing iisnode log files so we can delete the temp
+ // copies when we're finished packaging
+ Dictionary<string, string> tempDirectories = new Dictionary<string, string>();
+ try
+ {
+ string roles =
+ // Get the names of all web and worker roles
+ Enumerable.Concat(
+ definition.WebRole.NonNull().Select(role => role.name),
+ definition.WorkerRole.NonNull().Select(role => role.name))
+ // Get the name and safe path for each role (i.e., if the
+ // role has files that shouldn't be packaged, it'll be
+ // copied to a temp location without those files)
+ .Select(name => GetOrCreateCleanPath(rootPath, name, tempDirectories))
+ // Format the role name and path as a role argument
+ .Select(nameAndPath => string.Format(Resources.RoleArgTemplate, nameAndPath.Key, nameAndPath.Value))
+ // Join all the role arguments together into one
+ .DefaultIfEmpty(string.Empty)
+ .Aggregate(string.Concat);
+
+ string sites =
+ // Get all of the web roles
+ definition.WebRole.NonNull()
+ // Get all the sites in each role and format them all as
+ // site arguments
+ .SelectMany(role =>
+ // Format each site as a site argument
+ role.Sites.Site.Select(site =>
+ string.Format(
+ Resources.SitesArgTemplate,
+ role.name,
+ site.name,
+ tempDirectories.GetValueOrDefault(role.name, rootPath))))
+ // Join all the site arguments together into one
+ .DefaultIfEmpty(string.Empty)
+ .Aggregate(string.Concat);
+
+ string args = string.Format(
+ type == DevEnv.Local ? Resources.CsPackLocalArg : Resources.CsPackCloudArg,
+ rootPath,
+ roles,
+ sites);
+
+ // Run CsPack to generate the package
+ ProcessHelper.StartAndWaitForProcess(
+ new ProcessStartInfo(
+ Path.Combine(AzureSdkBinDirectory, Resources.CsPackExe),
+ args),
+ out standardOutput,
+ out standardError);
+ }
+ finally
+ {
+ // Cleanup any temp directories
+ tempDirectories.Values.ForEach(dir => Directory.Delete(dir, true));
}
-
- arguments = string.Format((type == DevEnv.Local) ? Resources.CsPackLocalArg : Resources.CsPackCloudArg, rootPath, rolesArg, sitesArg);
- return arguments;
- }
-
- [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
- private void Execute(string arguments, out string standardOutput, out string standardError)
- {
- ProcessStartInfo pi = new ProcessStartInfo(cspackPath, arguments);
- ProcessHelper.StartAndWaitForProcess(pi, out standardOutput, out standardError);
+ }
+
+ /// <summary>
+ /// Get or create a path to the role. This is used to sanitize the
+ /// contents of a role before it's packaged if it contains files that
+ /// shouldn't be packaged. We copy the contents to a temp directory,
+ /// delete the offending files, and use the temp directory for
+ /// packaging. The temp directories are collected in the
+ /// tempDirectories list so they can be cleaned up when packaging is
+ /// complete.
+ /// </summary>
+ /// <remarks>
+ /// This is a temporary workaround to prevent node logging information
+ /// from being packaged and deployed with production applications.
+ /// This method should be removed when we have a proper fix to bug
+ /// https://github.com/WindowsAzure/azure-sdk-tools/issues/111
+ /// </remarks>
+ /// <param name="root">The root path.</param>
+ /// <param name="name">Name of the role.</param>
+ /// <param name="tempDirectories">
+ /// A collection of temporary directories that have been created to
+ /// remove files that should not be published. This will be added to
+ /// by GetOrCreateCleanPath if a new directory is created that should
+ /// be cleaned up once the package has been generated.
+ /// </param>
+ /// <returns>
+ /// A pair containing the path to the role and the name of the role.
+ /// </returns>
+ private static KeyValuePair<string, string> GetOrCreateCleanPath(string root, string name, Dictionary<string, string> tempDirectories)
+ {
+ string path = Path.Combine(root, name);
+
+ // Check if the role has any "*.logs" directories that iisnode may
+ // have left during emulation
+ if (GetLogDirectories(path).Length == 0)
+ {
+ return new KeyValuePair<string, string>(name, root);
+ }
+ else
+ {
+ // Create a temporary directory
+ string tempPath = General.CreateTempDirectory();
+ tempDirectories[name] = tempPath;
+
+ // Copy the role's directory to the temp directory
+ string newPath = Path.Combine(tempPath, name);
+ General.CopyDirectory(Path.Combine(root, name), newPath);
+
+ // Remove the offending files
+ GetLogDirectories(newPath)
+ .ForEach(dir => Directory.Delete(dir, true));
+
+ return new KeyValuePair<string, string>(name, tempPath);
+ }
+ }
+
+ /// <summary>
+ /// Get the paths for any iisnode log directories under a given path.
+ /// </summary>
+ /// <param name="path">The path to search.</param>
+ /// <returns>Paths to any log directories.</returns>
+ private static string[] GetLogDirectories(string path)
+ {
+ return Directory.GetDirectories(path, "*.logs", SearchOption.AllDirectories);
}
}
-}
+}
Oops, something went wrong.

0 comments on commit a82edc0

Please sign in to comment.