public async Task <FunctionEnvelope> CreateOrUpdateAsync(string name, FunctionEnvelope functionEnvelope, Action setConfigChanged) { var functionDir = Path.Combine(_environment.FunctionsPath, name); // Make sure the function folder exists if (!FileSystemHelpers.DirectoryExists(functionDir)) { // Cleanup any leftover artifacts from a function with the same name before. DeleteFunctionArtifacts(name); FileSystemHelpers.EnsureDirectory(functionDir); } string newConfig = null; string configPath = Path.Combine(functionDir, Constants.FunctionsConfigFile); string dataFilePath = GetFunctionTestDataFilePath(name); // If files are included, write them out if (functionEnvelope?.Files != null) { // If the config is passed in the file collection, save it and don't process it as a file if (functionEnvelope.Files.TryGetValue(Constants.FunctionsConfigFile, out newConfig)) { functionEnvelope.Files.Remove(Constants.FunctionsConfigFile); } // Delete all existing files in the directory. This will also delete current function.json, but it gets recreated below FileSystemHelpers.DeleteDirectoryContentsSafe(functionDir); await Task.WhenAll(functionEnvelope.Files.Select(e => FileSystemHelpers.WriteAllTextToFileAsync(Path.Combine(functionDir, e.Key), e.Value))); } // Get the config (if it was not already passed in as a file) if (newConfig == null && functionEnvelope?.Config != null) { newConfig = JsonConvert.SerializeObject(functionEnvelope?.Config, Formatting.Indented); } // Get the current config, if any string currentConfig = null; if (FileSystemHelpers.FileExists(configPath)) { currentConfig = await FileSystemHelpers.ReadAllTextFromFileAsync(configPath); } // Save the file and set changed flag is it has changed. This helps optimize the syncTriggers call if (newConfig != currentConfig) { await FileSystemHelpers.WriteAllTextToFileAsync(configPath, newConfig); setConfigChanged(); } if (functionEnvelope?.TestData != null) { await FileSystemHelpers.WriteAllTextToFileAsync(dataFilePath, functionEnvelope.TestData); } return(await GetFunctionConfigAsync(name)); // test_data took from incoming request, it will not exceed the limit }
private string RefreshShutdownNotificationFilePath(string jobName, string jobsTypePath) { string shutdownFilesDirectory = Path.Combine(Environment.TempPath, "JobsShutdown", jobsTypePath, jobName); FileSystemHelpers.DeleteDirectoryContentsSafe(shutdownFilesDirectory, ignoreErrors: true); return(Path.Combine(shutdownFilesDirectory, Path.GetRandomFileName())); }
/// <summary> /// Specifically used for Linux Consumption to support Server Side build scenario /// </summary> /// <param name="context"></param> public static async Task SetupLinuxConsumptionFunctionAppDeployment( IEnvironment env, IDeploymentSettingsManager settings, DeploymentContext context, bool shouldSyncTriggers) { string sas = settings.GetValue(Constants.ScmRunFromPackage) ?? System.Environment.GetEnvironmentVariable(Constants.ScmRunFromPackage); string builtFolder = context.OutputPath; string packageFolder = env.DeploymentsPath; string packageFileName = OryxBuildConstants.FunctionAppBuildSettings.LinuxConsumptionArtifactName; // Package built content from oryx build artifact string filePath = PackageArtifactFromFolder(env, settings, context, builtFolder, packageFolder, packageFileName); // Log function app dependencies to kusto (requirements.txt, package.json, .csproj) await LogDependenciesFile(context.RepositoryPath); // Upload from DeploymentsPath await UploadLinuxConsumptionFunctionAppBuiltContent(context, sas, filePath); // Clean up local built content FileSystemHelpers.DeleteDirectoryContentsSafe(context.OutputPath); // Remove Linux consumption plan functionapp workers for the site await RemoveLinuxConsumptionFunctionAppWorkers(context); // Invoke a warmup call to the main function site if (shouldSyncTriggers) { await Task.Delay(TimeSpan.FromSeconds(5)); await FunctionHostSyncTriggersAsync(context); } }
public static void CopyWithManifest(string sourcePath, string destinationPath, IDeploymentManifestReader previousManifest, bool skipOldFiles = true) { sourcePath = Path.GetFullPath(sourcePath); destinationPath = Path.GetFullPath(destinationPath); using (var progressWriter = new ProgressWriter()) { progressWriter.Start(); if (previousManifest != null) { var previousFiles = new HashSet <string>(previousManifest.GetPaths(), StringComparer.OrdinalIgnoreCase); SmartCopy(sourcePath, destinationPath, previousFiles.Contains, new DirectoryInfoWrapper(new DirectoryInfo(sourcePath)), new DirectoryInfoWrapper(new DirectoryInfo(destinationPath)), path => new DirectoryInfoWrapper(new DirectoryInfo(path))); } else { // On first deployment, delete the contents of the destination path before copying FileSystemHelpers.DeleteDirectoryContentsSafe(destinationPath); // If there's no manifest then there's nothing to copy FileSystemHelpers.Copy(sourcePath, destinationPath); } } }
public async Task <bool> UninstallExtension(string id) { ITracer tracer = _traceFactory.GetTracer(); string installationDirectory = GetInstallationDirectory(id); SiteExtensionInfo info = await GetLocalExtension(id, checkLatest : false); if (info == null || !FileSystemHelpers.DirectoryExists(info.LocalPath)) { tracer.TraceError("Site extension {0} not found.", id); throw new DirectoryNotFoundException(installationDirectory); } if (IsInstalledToWebRoot(id)) { // special handling for package that install in wwwroot instead of under site extension folder tracer.Trace("Clear all content in wwwroot"); OperationManager.Attempt(() => FileSystemHelpers.DeleteDirectoryContentsSafe(_environment.WebRootPath)); } else { // default action for site extension uninstall // only site extension has below infomation var externalCommandFactory = new ExternalCommandFactory(_environment, _settings, installationDirectory); string uninstallScript = Path.Combine(installationDirectory, _uninstallScriptName); if (FileSystemHelpers.FileExists(uninstallScript)) { using (tracer.Step("Execute uninstall.cmd")) { OperationManager.Attempt(() => { Executable exe = externalCommandFactory.BuildCommandExecutable(uninstallScript, installationDirectory, _settings.GetCommandIdleTimeout(), NullLogger.Instance); exe.ExecuteWithProgressWriter(NullLogger.Instance, _traceFactory.GetTracer(), String.Empty); }); } } using (tracer.Step("Remove site extension job")) { OperationManager.Attempt(() => CleanupSiteExtensionJobs(id)); } } using (tracer.Step("Delete site extension package, directory and arm settings")) { OperationManager.Attempt(() => FileSystemHelpers.DeleteFileSafe(GetNuGetPackageFile(info.Id, info.Version))); OperationManager.Attempt(() => FileSystemHelpers.DeleteDirectorySafe(installationDirectory)); SiteExtensionStatus armSettings = new SiteExtensionStatus(_environment.SiteExtensionSettingsPath, id, tracer); await armSettings.RemoveStatus(); } return(await GetLocalExtension(id, checkLatest : false) == null); }
public void Delete(int deleteWebRoot = 0, int ignoreErrors = 0) { // Fail if a deployment is in progress bool acquired = _deploymentLock.TryLockOperation(() => { using (_tracer.Step("Deleting repository")) { string repositoryPath = Path.Combine(_environment.SiteRootPath, Constants.RepositoryPath); if (String.Equals(repositoryPath, _environment.RepositoryPath, StringComparison.OrdinalIgnoreCase)) { // Delete the repository FileSystemHelpers.DeleteDirectorySafe(_environment.RepositoryPath, ignoreErrors != 0); } else { // Just delete .git folder FileSystemHelpers.DeleteDirectorySafe(Path.Combine(_environment.RepositoryPath, ".git"), ignoreErrors != 0); FileSystemHelpers.DeleteDirectorySafe(Path.Combine(_environment.RepositoryPath, ".hg"), ignoreErrors != 0); } } using (_tracer.Step("Deleting ssh key")) { // Delete the ssh key FileSystemHelpers.DeleteDirectorySafe(_environment.SSHKeyPath, ignoreErrors != 0); } if (deleteWebRoot != 0) { using (_tracer.Step("Deleting web root")) { // Delete the wwwroot folder FileSystemHelpers.DeleteDirectoryContentsSafe(_environment.WebRootPath, ignoreErrors != 0); } using (_tracer.Step("Deleting diagnostics")) { // Delete the diagnostic log. This is a slight abuse of deleteWebRoot, but the // real semantic is more to reset the site to a fully clean state FileSystemHelpers.DeleteDirectorySafe(_environment.DiagnosticsPath, ignoreErrors != 0); } } using (_tracer.Step("Deleting deployment cache")) { // Delete the deployment cache FileSystemHelpers.DeleteDirectorySafe(_environment.DeploymentsPath, ignoreErrors != 0); } }, TimeSpan.Zero); if (!acquired) { HttpResponseMessage response = Request.CreateErrorResponse(HttpStatusCode.Conflict, Resources.Error_DeploymentInProgess); throw new HttpResponseException(response); } }
public void RemoveAllFilesInDirectory(string directoryPath, StorageLocation location, string blobSasUri) { if (UseBlobStorage(location, blobSasUri)) { return; } string baseDir = GetRootStoragePathForWhenBlobStorageIsNotConfigured(location); string fullPath = Path.Combine(baseDir, directoryPath); if (System.IO.File.Exists(fullPath)) { FileSystemHelpers.DeleteDirectoryContentsSafe(fullPath); } }
/// <summary> /// Specifically used for Linux Consumption to support Server Side build scenario /// </summary> /// <param name="context"></param> public static async Task SetupLinuxConsumptionFunctionAppDeployment( IEnvironment env, IDeploymentSettingsManager settings, DeploymentContext context, bool shouldSyncTriggers, bool shouldUpdateWebsiteRunFromPackage) { string builtFolder = context.OutputPath; string packageFolder = env.ArtifactsPath; string packageFileName = OryxBuildConstants.FunctionAppBuildSettings.LinuxConsumptionArtifactName; // Indicate the memory limitation on Linux Consumption context.Logger.Log($"Linux Consumption plan has a 1.5 GB memory limit on a remote build container."); context.Logger.Log($"To check our service limit, please visit https://docs.microsoft.com/en-us/azure/azure-functions/functions-scale#service-limits"); // Try create a placeholder blob in AzureWebJobsStorage CloudBlockBlob blob = await GetPlaceholderBlob(context, settings, shouldUpdateWebsiteRunFromPackage); // Package built content from oryx build artifact string filePath = PackageArtifactFromFolder(env, settings, context, builtFolder, packageFolder, packageFileName); // Log function app dependencies to kusto (requirements.txt, package.json, .csproj) await LogDependenciesFile(context.RepositoryPath); // Upload from DeploymentsPath await UploadLinuxConsumptionFunctionAppBuiltContent(context, blob, filePath); // Clean up local built content FileSystemHelpers.DeleteDirectoryContentsSafe(context.OutputPath); // Change WEBSITE_RUN_FROM_PACKAGE app setting if (shouldUpdateWebsiteRunFromPackage) { await UpdateWebsiteRunFromPackageAppSetting(context, blob); } // Remove Linux consumption plan functionapp workers for the site await RemoveLinuxConsumptionFunctionAppWorkers(context); // Invoke a warmup call to the main function site if (shouldSyncTriggers) { await Task.Delay(TimeSpan.FromSeconds(5)); await FunctionHostSyncTriggersAsync(context); } }
public async Task RemoveStatus() { try { string dirName = Path.GetDirectoryName(_filePath); if (FileSystemHelpers.DirectoryExists(dirName)) { FileSystemHelpers.DeleteDirectoryContentsSafe(dirName); // call DeleteDirectorySafe directly would sometime causing "Access denied" on folder // work-around: remove content and wait briefly before delete folder await Task.Delay(300); FileSystemHelpers.DeleteDirectorySafe(dirName); } } catch (Exception ex) { // no-op _tracer.TraceError(ex); } }
/// <summary> /// <para>Check if there is damanged leftover data</para> /// <para>If there is leftover data, will try to cleanup.</para> /// <para>If fail to cleanup, will move leftover data to Temp folder</para> /// </summary> private static void EnsureInstallationEnviroment(string installationDir, ITracer tracer) { // folder is there but nupkg is gone, means previous uninstallation must encounter some error bool isInstalledPackageBroken = FileSystemHelpers.DirectoryExists(installationDir) && FileSystemHelpers.GetFiles(installationDir, "*.nupkg").Length == 0; if (!isInstalledPackageBroken) { return; } using (tracer.Step("There was leftover data from previous uninstallation. Trying to cleanup now.")) { try { OperationManager.Attempt(() => FileSystemHelpers.DeleteDirectorySafe(installationDir, ignoreErrors: false)); return; } catch (Exception ex) { tracer.TraceError(ex); } FileSystemHelpers.EnsureDirectory(_toBeDeletedDirectoryPath); DirectoryInfo dirInfo = new DirectoryInfo(installationDir); string tmpFoder = Path.Combine( _toBeDeletedDirectoryPath, string.Format(CultureInfo.InvariantCulture, "{0}-{1}", dirInfo.Name, Guid.NewGuid().ToString("N").Substring(0, 8))); using (tracer.Step("Failed to cleanup. Moving leftover data to {0}", tmpFoder)) { // if failed, let exception bubble up to trigger bad request OperationManager.Attempt(() => FileSystemHelpers.MoveDirectory(installationDir, tmpFoder)); } } FileSystemHelpers.DeleteDirectoryContentsSafe(_toBeDeletedDirectoryPath); }
public void Delete(int deleteWebRoot = 0, int ignoreErrors = 0) { try { // Fail if a deployment is in progress _deploymentLock.LockOperation(() => { using (_tracer.Step("Deleting repository")) { string repositoryPath = Path.Combine(_environment.SiteRootPath, Constants.RepositoryPath); if (String.Equals(repositoryPath, _environment.RepositoryPath, StringComparison.OrdinalIgnoreCase)) { // Delete the repository FileSystemHelpers.DeleteDirectorySafe(_environment.RepositoryPath, ignoreErrors != 0); } else { // Just delete .git folder FileSystemHelpers.DeleteDirectorySafe(Path.Combine(_environment.RepositoryPath, ".git"), ignoreErrors != 0); FileSystemHelpers.DeleteDirectorySafe(Path.Combine(_environment.RepositoryPath, ".hg"), ignoreErrors != 0); } } using (_tracer.Step("Delete auto swap lock file")) { FileSystemHelpers.DeleteFileSafe(Path.Combine(_environment.LocksPath, AutoSwapHandler.AutoSwapLockFile)); } using (_tracer.Step("Deleting ssh key")) { // Delete the ssh key FileSystemHelpers.DeleteDirectorySafe(_environment.SSHKeyPath, ignoreErrors != 0); } if (deleteWebRoot != 0) { // This logic is primarily used to help with site reuse during test. // The flag is not documented for general use. using (_tracer.Step("Deleting web root")) { // Delete the wwwroot folder FileSystemHelpers.DeleteDirectoryContentsSafe(_environment.WebRootPath, ignoreErrors != 0); } using (_tracer.Step("Deleting diagnostics")) { // Delete the diagnostic log. This is a slight abuse of deleteWebRoot, but the // real semantic is more to reset the site to a fully clean state FileSystemHelpers.DeleteDirectorySafe(_environment.DiagnosticsPath, ignoreErrors != 0); } using (_tracer.Step("Deleting ASP.NET 5 approot")) { // Delete the approot folder used by ASP.NET 5 apps FileSystemHelpers.DeleteDirectorySafe(Path.Combine(_environment.SiteRootPath, "approot"), ignoreErrors != 0); } // Delete first deployment manifest since it is no longer needed FileSystemHelpers.DeleteFileSafe(Path.Combine(_environment.SiteRootPath, Constants.FirstDeploymentManifestFileName)); } else { using (_tracer.Step("Updating initial deployment manifest")) { // The active deployment manifest becomes the baseline initial deployment manifest // When SCM is reconnected, the new deployment will use this manifest to clean the wwwroot SaveInitialDeploymentManifest(); } } using (_tracer.Step("Deleting deployment cache")) { // Delete the deployment cache FileSystemHelpers.DeleteDirectorySafe(_environment.DeploymentsPath, ignoreErrors != 0); } }, "Deleting repository", TimeSpan.Zero); } catch (LockOperationException ex) { HttpResponseMessage response = Request.CreateErrorResponse(HttpStatusCode.Conflict, ex.Message); throw new HttpResponseException(response); } }