public void ReturnsEmptyWhenNoDeploymentsPathConfigured()
        {
            var fileSystem = new Mock <IMeshPersistentFileSystem>(MockBehavior.Strict);

            fileSystem.Setup(f => f.GetDeploymentsPath()).Returns(string.Empty);

            var fileSystemPathProvider = new FileSystemPathProvider(fileSystem.Object);

            Assert.False(fileSystemPathProvider.TryGetDeploymentsPath(out string actualDeploymentsPath));
            Assert.Equal(string.Empty, actualDeploymentsPath);
        }
        public void ReturnsDeploymentsPath()
        {
            const string deploymentsPath = "/path-0";

            var mockFileSystem = new Mock <IFileSystem>(MockBehavior.Strict);
            var directory      = new Mock <DirectoryBase>(MockBehavior.Strict);

            directory.Setup(d => d.Exists(deploymentsPath)).Returns(true);
            mockFileSystem.SetupGet(f => f.Directory).Returns(directory.Object);

            using (new MockFileSystem(mockFileSystem.Object))
            {
                var          fileSystem = new Mock <IMeshPersistentFileSystem>(MockBehavior.Strict);
                const string expectedDeploymentsPath = deploymentsPath;
                fileSystem.Setup(f => f.GetDeploymentsPath()).Returns(expectedDeploymentsPath);

                var fileSystemPathProvider = new FileSystemPathProvider(fileSystem.Object);
                Assert.True(fileSystemPathProvider.TryGetDeploymentsPath(out string actualDeploymentsPath));
                Assert.Equal(expectedDeploymentsPath, actualDeploymentsPath);
            }
        }
        public void ReturnsEmptyPathWhenDeploymentDirectoryCreationFails()
        {
            const string deploymentsPath = "/path-0";

            var mockFileSystem = new Mock <IFileSystem>(MockBehavior.Strict);
            var directory      = new Mock <DirectoryBase>(MockBehavior.Strict);

            directory.Setup(d => d.Exists(deploymentsPath)).Returns(false);
            directory.Setup(d => d.CreateDirectory(deploymentsPath)).Throws(new IOException());
            mockFileSystem.SetupGet(f => f.Directory).Returns(directory.Object);

            using (new MockFileSystem(mockFileSystem.Object))
            {
                var fileSystem = new Mock <IMeshPersistentFileSystem>(MockBehavior.Strict);
                fileSystem.Setup(f => f.GetDeploymentsPath()).Returns(deploymentsPath);

                var fileSystemPathProvider = new FileSystemPathProvider(fileSystem.Object);
                Assert.False(fileSystemPathProvider.TryGetDeploymentsPath(out string actualDeploymentsPath));
                Assert.Equal(deploymentsPath, actualDeploymentsPath);
            }
        }
Example #4
0
        /// <summary>
        /// This method gets called by the runtime. It is used to add services
        /// to the container. It uses the Extension pattern.
        /// </summary>
        /// <todo>
        ///   CORE TODO Remove initializing contextAccessor : See if over time we can refactor away the need for this?
        ///   It's kind of a quick hack/compatibility shim. Ideally you want to get the request context only from where
        ///   it's specifically provided to you (Request.HttpContext in a controller, or as an Invoke() parameter in
        ///   a middleware) and pass it wherever its needed.
        /// </todo>
        public void ConfigureServices(IServiceCollection services)
        {
            Console.WriteLine(@"Configure Services : " + DateTime.Now.ToString("hh.mm.ss.ffffff"));
            FileSystemHelpers.DeleteDirectorySafe("/home/site/locks/deployment");
            services.Configure <FormOptions>(options =>
            {
                options.MultipartBodyLengthLimit = 52428800;
                options.ValueCountLimit          = 500000;
                options.KeyLengthLimit           = 500000;
            });

            services.AddRouteAnalyzer();

            // Kudu.Services contains all the Controllers
            var kuduServicesAssembly = Assembly.Load("Kudu.Services");

            services.AddMvcCore()
            .AddRazorPages()
            .AddAuthorization()
            .AddJsonFormatters()
            .AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver())
            .AddApplicationPart(kuduServicesAssembly).AddControllersAsServices()
            .AddApiExplorer();

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info {
                    Title = "Kudu API Docs"
                });
                // Setting the comments path for the Swagger JSON and UI.
                var xmlFile = $"Kudu.Services.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                c.IncludeXmlComments(xmlPath);
            });

            services.AddGZipCompression();

            services.AddDirectoryBrowser();

            services.AddDataProtection();

            services.AddLogging(
                builder =>
            {
                builder.AddFilter("Microsoft", LogLevel.Warning)
                .AddFilter("System", LogLevel.Warning)
                .AddConsole();
            });

            services.AddSingleton <IHttpContextAccessor>(new HttpContextAccessor());
            services.TryAddSingleton <ISystemEnvironment>(SystemEnvironment.Instance);
            services.AddSingleton <ILinuxConsumptionEnvironment, LinuxConsumptionEnvironment>();
            services.AddSingleton <ILinuxConsumptionInstanceManager, LinuxConsumptionInstanceManager>();
            services.AddSingleton <IFileSystemPathProvider, FileSystemPathProvider>();
            services.AddSingleton <IStorageClient, StorageClient>();

            KuduWebUtil.EnsureHomeEnvironmentVariable();

            KuduWebUtil.EnsureSiteBitnessEnvironmentVariable();

            var fileSystemPathProvider = new FileSystemPathProvider(new MeshPersistentFileSystem(SystemEnvironment.Instance,
                                                                                                 new MeshServiceClient(SystemEnvironment.Instance, new HttpClient()), new StorageClient(SystemEnvironment.Instance)));
            IEnvironment environment = KuduWebUtil.GetEnvironment(_hostingEnvironment, fileSystemPathProvider);

            _webAppRuntimeEnvironment = environment;

            services.AddSingleton(_ => new HttpClient());
            services.AddSingleton <IMeshServiceClient>(s =>
            {
                if (environment.IsOnLinuxConsumption)
                {
                    var httpClient        = s.GetService <HttpClient>();
                    var systemEnvironment = s.GetService <ISystemEnvironment>();
                    return(new MeshServiceClient(systemEnvironment, httpClient));
                }
                else
                {
                    return(new NullMeshServiceClient());
                }
            });
            services.AddSingleton <IMeshPersistentFileSystem>(s =>
            {
                if (environment.IsOnLinuxConsumption)
                {
                    var meshServiceClient = s.GetService <IMeshServiceClient>();
                    var storageClient     = s.GetService <IStorageClient>();
                    var systemEnvironment = s.GetService <ISystemEnvironment>();
                    return(new MeshPersistentFileSystem(systemEnvironment, meshServiceClient, storageClient));
                }
                else
                {
                    return(new NullMeshPersistentFileSystem());
                }
            });

            KuduWebUtil.EnsureDotNetCoreEnvironmentVariable(environment);

            // CORE TODO Check this
            // fix up invalid /home/site/deployments/settings.xml
            KuduWebUtil.EnsureValidDeploymentXmlSettings(environment);

            // Add various folders that never change to the process path. All child processes will inherit this
            KuduWebUtil.PrependFoldersToPath(environment);

            // Add middleware for Linux Consumption authentication and authorization
            // when KuduLIte is running in service fabric mesh
            services.AddLinuxConsumptionAuthentication();
            services.AddLinuxConsumptionAuthorization(environment);

            // General
            services.AddSingleton <IServerConfiguration, ServerConfiguration>();

            // CORE TODO Looks like this doesn't ever actually do anything, can refactor out?
            services.AddSingleton <IBuildPropertyProvider>(new BuildPropertyProvider());

            _noContextDeploymentsSettingsManager =
                new DeploymentSettingsManager(new XmlSettings.Settings(KuduWebUtil.GetSettingsPath(environment)));
            TraceServices.TraceLevel = _noContextDeploymentsSettingsManager.GetTraceLevel();

            // Per request environment
            services.AddScoped(sp =>
                               KuduWebUtil.GetEnvironment(_hostingEnvironment, sp.GetRequiredService <IFileSystemPathProvider>(), sp.GetRequiredService <IDeploymentSettingsManager>()));

            services.AddDeploymentServices(environment);

            /*
             * CORE TODO Refactor ITracerFactory/ITracer/GetTracer()/
             * ILogger needs serious refactoring:
             * - Names should be changed to make it clearer that ILogger is for deployment
             *   logging and ITracer and friends are for Kudu tracing
             * - ILogger is a first-class citizen in .NET core and has it's own meaning. We should be using it
             *   where appropriate (and not name-colliding with it)
             * - ITracer vs. ITraceFactory is redundant and confusing.
             * - TraceServices only serves to confuse stuff now that we're avoiding
             */
            Func <IServiceProvider, ITracer> resolveTracer = KuduWebUtil.GetTracer;

            ITracer CreateTracerThunk() => resolveTracer(services.BuildServiceProvider());

            // First try to use the current request profiler if any, otherwise create a new one
            var traceFactory = new TracerFactory(() =>
            {
                var sp      = services.BuildServiceProvider();
                var context = sp.GetRequiredService <IHttpContextAccessor>().HttpContext;
                return(TraceServices.GetRequestTracer(context) ?? resolveTracer(sp));
            });

            services.AddScoped <ITracer>(sp =>
            {
                var context = sp.GetRequiredService <IHttpContextAccessor>().HttpContext;
                return(TraceServices.GetRequestTracer(context) ?? NullTracer.Instance);
            });

            services.AddSingleton <ITraceFactory>(traceFactory);

            TraceServices.SetTraceFactory(CreateTracerThunk);

            services.AddSingleton <IDictionary <string, IOperationLock> >(
                KuduWebUtil.GetNamedLocks(traceFactory, environment));

            // CORE TODO ShutdownDetector, used by LogStreamManager.
            //var shutdownDetector = new ShutdownDetector();
            //shutdownDetector.Initialize()

            var noContextTraceFactory = new TracerFactory(() =>
                                                          KuduWebUtil.GetTracerWithoutContext(environment, _noContextDeploymentsSettingsManager));

            services.AddTransient <IAnalytics>(sp => new Analytics(sp.GetRequiredService <IDeploymentSettingsManager>(),
                                                                   sp.GetRequiredService <IServerConfiguration>(),
                                                                   noContextTraceFactory));

            // CORE TODO
            // Trace shutdown event
            // Cannot use shutdownDetector.Token.Register because of race condition
            // with NinjectServices.Stop via WebActivator.ApplicationShutdownMethodAttribute
            // Shutdown += () => TraceShutdown(environment, noContextDeploymentsSettingsManager);

            // LogStream service
            services.AddLogStreamService(_webAppRuntimeEnvironment, traceFactory);

            // Deployment Service
            services.AddWebJobsDependencies();

            services.AddScoped <ILogger>(KuduWebUtil.GetDeploymentLogger);

            services.AddScoped <IDeploymentManager, DeploymentManager>();

            services.AddScoped <IFetchDeploymentManager, FetchDeploymentManager>();

            services.AddScoped <IScanManager, ScanManager>();

            services.AddScoped <ISSHKeyManager, SSHKeyManager>();

            services.AddScoped <IRepositoryFactory>(
                sp => KuduWebUtil.GetDeploymentLock(traceFactory, environment).RepositoryFactory =
                    new RepositoryFactory(
                        sp.GetRequiredService <IEnvironment>(), sp.GetRequiredService <IDeploymentSettingsManager>(),
                        sp.GetRequiredService <ITraceFactory>()));

            services.AddScoped <IApplicationLogsReader, ApplicationLogsReader>();

            // Git server
            services.AddGitServer(KuduWebUtil.GetDeploymentLock(traceFactory, environment));

            // Git Servicehook Parsers
            services.AddGitServiceHookParsers();

            services.AddScoped <ICommandExecutor, CommandExecutor>();

            // KuduWebUtil.MigrateSite(environment, noContextDeploymentsSettingsManager);
            // RemoveOldTracePath(environment);
            // RemoveTempFileFromUserDrive(environment);

            // CORE TODO Windows Fix: Temporary fix for https://github.com/npm/npm/issues/5905
            //EnsureNpmGlobalDirectory();
            //EnsureUserProfileDirectory();

            //// Skip SSL Certificate Validate
            //if (Environment.SkipSslValidation)
            //{
            //    ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
            //}

            //// Make sure webpages:Enabled is true. Even though we set it in web.config, it could be overwritten by
            //// an Azure AppSetting that's supposed to be for the site only but incidently affects Kudu as well.
            ConfigurationManager.AppSettings["webpages:Enabled"] = "true";

            //// Kudu does not rely owin:appStartup.  This is to avoid Azure AppSetting if set.
            if (ConfigurationManager.AppSettings?["owin:appStartup"] != null)
            {
                // Set the appSetting to null since we cannot use AppSettings.Remove(key) (ReadOnly exception!)
                ConfigurationManager.AppSettings["owin:appStartup"] = null;
            }

            //RegisterRoutes(kernel, RouteTable.Routes);

            //// Register the default hubs route: ~/signalr
            //GlobalHost.DependencyResolver = new SignalRNinjectDependencyResolver(kernel);
            //GlobalConfiguration.Configuration.Filters.Add(
            //    new TraceDeprecatedActionAttribute(
            //        kernel.Get<IAnalytics>(),
            //        kernel.Get<ITraceFactory>()));
            //GlobalConfiguration.Configuration.Filters.Add(new EnsureRequestIdHandlerAttribute());

            //FileTarget target = LogManager.Configuration.FindTargetByName("file") as FileTarget;
            //String logfile = _webAppRuntimeEnvironment.LogFilesPath + "/.txt";
            //target.FileName = logfile;
        }