private async Task TransformProjectToContainer(Model.Application application, Model.Service service, ProjectRunInfo project) { var serviceDescription = service.Description; var serviceName = serviceDescription.Name; service.Status.ProjectFilePath = project.ProjectFile.FullName; var targetFramework = project.TargetFramework; // Sometimes building can fail because of file locking (like files being open in VS) _logger.LogInformation("Publishing project {ProjectFile}", service.Status.ProjectFilePath); var publishCommand = $"publish \"{service.Status.ProjectFilePath}\" --framework {targetFramework} /nologo"; service.Logs.OnNext($"dotnet {publishCommand}"); var buildResult = await ProcessUtil.RunAsync("dotnet", publishCommand, throwOnError : false); service.Logs.OnNext(buildResult.StandardOutput); if (buildResult.ExitCode != 0) { _logger.LogInformation("Publishing {ProjectFile} failed with exit code {ExitCode}: \r\n" + buildResult.StandardOutput, service.Status.ProjectFilePath, buildResult.ExitCode); // Null out the RunInfo so that serviceDescription.RunInfo = null; return; } // We transform the project information into the following docker command: // docker run -w /app -v {publishDir}:/app -it {image} dotnet {outputfile}.dll // We swap the slashes since we're going to run on linux var containerImage = DetermineContainerImage(targetFramework); var outputFileName = project.AssemblyName + ".dll"; var dockerRunInfo = new DockerRunInfo(containerImage, $"dotnet {outputFileName} {project.Args}") { WorkingDirectory = "/app" }; dockerRunInfo.VolumeMappings[project.PublishOutputPath] = "/app"; // Make volume mapping works when running as a container foreach (var mapping in project.VolumeMappings) { dockerRunInfo.VolumeMappings[mapping.Key] = mapping.Value; } // Change the project into a container info serviceDescription.RunInfo = dockerRunInfo; }
private async Task TransformProjectToContainer(Model.Application application, Model.Service service, ProjectRunInfo project) { var serviceDescription = service.Description; var serviceName = serviceDescription.Name; var expandedProject = Environment.ExpandEnvironmentVariables(project.Project); var fullProjectPath = Path.GetFullPath(Path.Combine(application.ContextDirectory, expandedProject)); service.Status.ProjectFilePath = fullProjectPath; // Sometimes building can fail because of file locking (like files being open in VS) _logger.LogInformation("Publishing project {ProjectFile}", service.Status.ProjectFilePath); service.Logs.OnNext($"dotnet publish \"{service.Status.ProjectFilePath}\" /nologo"); var buildResult = await ProcessUtil.RunAsync("dotnet", $"publish \"{service.Status.ProjectFilePath}\" /nologo", outputDataReceived : data => service.Logs.OnNext(data), throwOnError : false); if (buildResult.ExitCode != 0) { _logger.LogInformation("Publishing {ProjectFile} failed with exit code {ExitCode}: " + buildResult.StandardOutput + buildResult.StandardError, service.Status.ProjectFilePath, buildResult.ExitCode); return; } var targetFramework = GetTargetFramework(service.Status.ProjectFilePath); // We transform the project information into the following docker command: // docker run -w /app -v {projectDir}:/app -it {image} dotnet /app/bin/Debug/{tfm}/publish/{outputfile}.dll var containerImage = DetermineContainerImage(targetFramework); var outputFileName = Path.GetFileNameWithoutExtension(service.Status.ProjectFilePath) + ".dll"; var dockerRunInfo = new DockerRunInfo(containerImage, $"dotnet /app/bin/Debug/{targetFramework}/publish/{outputFileName} {project.Args}") { WorkingDirectory = "/app" }; dockerRunInfo.VolumeMappings[Path.GetDirectoryName(service.Status.ProjectFilePath) !] = "/app";
public void PopulateEnvironment(Service service, Action <string, string> set, string defaultHost = "localhost") { if (service.Description.Configuration != null) { // Inject normal configuration foreach (var pair in service.Description.Configuration) { if (pair.Value is object) { set(pair.Name, pair.Value); } else if (pair.Source is object) { set(pair.Name, GetValueFromBinding(pair.Source)); } } } string GetValueFromBinding(EnvironmentVariableSource source) { if (!Services.TryGetValue(source.Service, out var service)) { throw new InvalidOperationException($"Could not find service '{source.Service}'."); } var binding = service.Description.Bindings.Where(b => b.Name == source.Binding).FirstOrDefault(); if (binding == null) { throw new InvalidOperationException($"Could not find binding '{source.Binding}' for service '{source.Service}'."); } // TODO finish if (source.Kind == EnvironmentVariableSource.SourceKind.Port && binding.Port != null) { return(binding.Port.Value.ToString()); } else if (source.Kind == EnvironmentVariableSource.SourceKind.Host) { return(binding.Host ?? defaultHost); } else if (source.Kind == EnvironmentVariableSource.SourceKind.Url) { return($"{binding.Protocol ?? "http"}://{binding.Host ?? defaultHost}" + (binding.Port.HasValue ? (":" + binding.Port) : string.Empty)); } else if (source.Kind == EnvironmentVariableSource.SourceKind.ConnectionString && binding.ConnectionString != null) { return(binding.ConnectionString); } throw new InvalidOperationException($"Unable to resolve the desired value '{source.Kind}' from binding '{source.Binding}' for service '{source.Service}'."); } void SetBinding(ServiceDescription targetService, ServiceBinding b) { var serviceName = targetService.Name.ToUpper(); var configName = ""; var envName = ""; if (string.IsNullOrEmpty(b.Name)) { configName = serviceName; envName = serviceName; } else { configName = $"{serviceName.ToUpper()}__{b.Name.ToUpper()}"; envName = $"{serviceName.ToUpper()}_{b.Name.ToUpper()}"; } if (!string.IsNullOrEmpty(b.ConnectionString)) { // Special case for connection strings set($"CONNECTIONSTRING__{configName}", b.ConnectionString); } if (!string.IsNullOrEmpty(b.Protocol)) { // IConfiguration specific (double underscore ends up telling the configuration provider to use it as a separator) set($"SERVICE__{configName}__PROTOCOL", b.Protocol); set($"{envName}_SERVICE_PROTOCOL", b.Protocol); } if (b.Port != null) { var port = (service.Description.RunInfo is DockerRunInfo && targetService.RunInfo is DockerRunInfo) ? b.ContainerPort ?? b.Port.Value : b.Port.Value; set($"SERVICE__{configName}__PORT", port.ToString()); set($"{envName}_SERVICE_PORT", port.ToString()); } // Use the container name as the host name if there's a single replica (current limitation) var host = b.Host ?? (service.Description.RunInfo is DockerRunInfo && targetService.RunInfo is DockerRunInfo ? targetService.Name : defaultHost); set($"SERVICE__{configName}__HOST", host); set($"{envName}_SERVICE_HOST", host); } // Inject dependency information foreach (var s in Services.Values) { foreach (var b in s.Description.Bindings) { SetBinding(s.Description, b); } } }
private static V1Service CreateServiceJson(Model.Service service) { var description = service.Description; var bindings = description.Bindings; var v1bindingList = new List <V1ServiceBinding>(); foreach (var binding in bindings) { v1bindingList.Add(new V1ServiceBinding() { Name = binding.Name, ConnectionString = binding.ConnectionString, AutoAssignPort = binding.AutoAssignPort, Port = binding.Port, ContainerPort = binding.ContainerPort, Host = binding.Host, Protocol = binding.Protocol }); } var v1ConfigurationSourceList = new List <V1ConfigurationSource>(); foreach (var configSource in description.Configuration) { v1ConfigurationSourceList.Add(new V1ConfigurationSource() { Name = configSource.Name, Value = configSource.Value }); } var v1RunInfo = new V1RunInfo(); if (description.RunInfo is DockerRunInfo dockerRunInfo) { v1RunInfo.Type = V1RunInfoType.Docker; v1RunInfo.Image = dockerRunInfo.Image; v1RunInfo.VolumeMappings = dockerRunInfo.VolumeMappings.Select(v => new V1DockerVolume { Name = v.Name, Source = v.Source, Target = v.Target }).ToList(); v1RunInfo.WorkingDirectory = dockerRunInfo.WorkingDirectory; v1RunInfo.Args = dockerRunInfo.Args; } else if (description.RunInfo is ExecutableRunInfo executableRunInfo) { v1RunInfo.Type = V1RunInfoType.Executable; v1RunInfo.Args = executableRunInfo.Args; v1RunInfo.Executable = executableRunInfo.Executable; v1RunInfo.WorkingDirectory = executableRunInfo.WorkingDirectory; } else if (description.RunInfo is ProjectRunInfo projectRunInfo) { v1RunInfo.Type = V1RunInfoType.Project; v1RunInfo.Args = projectRunInfo.Args; v1RunInfo.Build = projectRunInfo.Build; v1RunInfo.Project = projectRunInfo.ProjectFile.FullName; } var v1ServiceDescription = new V1ServiceDescription() { Bindings = v1bindingList, Configuration = v1ConfigurationSourceList, Name = description.Name, Replicas = description.Replicas, RunInfo = v1RunInfo }; var replicateDictionary = new Dictionary <string, V1ReplicaStatus>(); foreach (var replica in service.Replicas) { var replicaStatus = new V1ReplicaStatus() { Name = replica.Value.Name, Ports = replica.Value.Ports, Environment = replica.Value.Environment }; replicateDictionary[replica.Key] = replicaStatus; if (replica.Value is ProcessStatus processStatus) { replicaStatus.Pid = processStatus.Pid; replicaStatus.ExitCode = processStatus.ExitCode; } else if (replica.Value is DockerStatus dockerStatus) { replicaStatus.DockerCommand = dockerStatus.DockerCommand; replicaStatus.ContainerId = dockerStatus.ContainerId; } } var v1Status = new V1ServiceStatus() { ProjectFilePath = service.Status.ProjectFilePath, ExecutablePath = service.Status.ExecutablePath, Args = service.Status.Args, WorkingDirectory = service.Status.WorkingDirectory, }; var serviceJson = new V1Service() { ServiceType = service.ServiceType, Status = v1Status, Description = v1ServiceDescription, Replicas = replicateDictionary, Restarts = service.Restarts }; return(serviceJson); }
private static V1Service CreateServiceJson(Model.Service service) { var description = service.Description; var bindings = description.Bindings; var v1bindingList = new List <V1ServiceBinding>(); foreach (var binding in bindings) { v1bindingList.Add(new V1ServiceBinding() { Name = binding.Name, ConnectionString = binding.ConnectionString, AutoAssignPort = binding.AutoAssignPort, Port = binding.Port, InternalPort = binding.InternalPort, Host = binding.Host, Protocol = binding.Protocol }); } var v1ConfigurationSourceList = new List <V1ConfigurationSource>(); foreach (var configSource in description.Configuration) { v1ConfigurationSourceList.Add(new V1ConfigurationSource() { Name = configSource.Name, Value = configSource.Value }); } var v1RunInfo = new V1RunInfo(); if (description.RunInfo is DockerRunInfo dockerRunInfo) { v1RunInfo.Type = V1RunInfoType.Docker; v1RunInfo.Image = dockerRunInfo.Image; v1RunInfo.VolumeMappings = dockerRunInfo.VolumeMappings; v1RunInfo.WorkingDirectory = dockerRunInfo.WorkingDirectory; v1RunInfo.Args = dockerRunInfo.Args; } else if (description.RunInfo is ExecutableRunInfo executableRunInfo) { v1RunInfo.Type = V1RunInfoType.Executable; v1RunInfo.Args = executableRunInfo.Args; v1RunInfo.Executable = executableRunInfo.Executable; v1RunInfo.WorkingDirectory = executableRunInfo.WorkingDirectory; } else if (description.RunInfo is ProjectRunInfo projectRunInfo) { v1RunInfo.Type = V1RunInfoType.Project; v1RunInfo.Args = projectRunInfo.Args; v1RunInfo.Build = projectRunInfo.Build; v1RunInfo.Project = projectRunInfo.Project; } var v1ServiceDescription = new V1ServiceDescription() { Bindings = v1bindingList, Configuration = v1ConfigurationSourceList, Name = description.Name, Replicas = description.Replicas, RunInfo = v1RunInfo }; var replicateDictionary = new Dictionary <string, V1ReplicaStatus>(); foreach (var replica in service.Replicas) { replicateDictionary[replica.Key] = new V1ReplicaStatus() { Name = replica.Value.Name, Ports = replica.Value.Ports }; } var v1Status = new V1ServiceStatus() { ProjectFilePath = service.Status.ProjectFilePath, ExecutablePath = service.Status.ExecutablePath, Args = service.Status.Args, WorkingDirectory = service.Status.WorkingDirectory, }; var serviceJson = new V1Service() { ServiceType = service.ServiceType, Status = v1Status, Description = v1ServiceDescription, Replicas = replicateDictionary, Restarts = service.Restarts }; return(serviceJson); }