This commit is contained in:
Ryan Nowak 2020-05-02 16:23:48 -07:00
Родитель d1d516da34
Коммит 5ab6d46160
17 изменённых файлов: 656 добавлений и 115 удалений

40
docs/recipes/zipkin.yaml Normal file
Просмотреть файл

@ -0,0 +1,40 @@
kind: Deployment
apiVersion: apps/v1
metadata:
name: zipkin
labels:
app.kubernetes.io/name: 'zipkin'
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: zipkin
template:
metadata:
labels:
app.kubernetes.io/name: 'zipkin'
spec:
containers:
- name: zipkin
image: openzipkin/zipkin
imagePullPolicy: Always
ports:
- containerPort: 9411
...
---
kind: Service
apiVersion: v1
metadata:
name: zipkin
labels:
app.kubernetes.io/name: 'zipkin'
spec:
selector:
app.kubernetes.io/name: zipkin
type: ClusterIP
ports:
- name: http
protocol: TCP
port: 9411
targetPort: 9411
...

Просмотреть файл

@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
@ -49,12 +50,22 @@ namespace Microsoft.Tye
// some duplication with the code in m8s (Application.cs) for populating environments.
//
// service.Service.Bindings is the bindings OUT - this step computes bindings IN.
var bindings = new ComputedBindings();
service.Outputs.Add(bindings);
service.Outputs.Add(ComputeBindings(application, service.Dependencies));
foreach (var o in service.Dependencies)
foreach (var sidecar in project.Sidecars)
{
var other = application.Services.Single(a => a.Name == o);
sidecar.Outputs.Add(ComputeBindings(application, sidecar.Dependencies));
}
return Task.CompletedTask;
}
private ComputedBindings ComputeBindings(ApplicationBuilder application, IEnumerable<string> dependencies)
{
var bindings = new ComputedBindings();
foreach (var dependency in dependencies)
{
var other = application.Services.Single(a => a.Name == dependency);
foreach (var binding in other.Bindings)
{
@ -126,7 +137,7 @@ namespace Microsoft.Tye
}
}
return Task.CompletedTask;
return bindings;
}
}
}

Просмотреть файл

@ -274,6 +274,36 @@ namespace Microsoft.Tye
spec = new YamlMappingNode();
template.Add("spec", spec);
if (project.Sidecars.Count > 0)
{
// Share process namespace when we have sidecars. So we can list other processes.
// see: https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/#understanding-process-namespace-sharing
spec.Add("shareProcessNamespace", new YamlScalarNode("true") { Style = ScalarStyle.Plain });
}
if (project.RelocateDiagnosticsDomainSockets)
{
// Our diagnostics functionality uses $TMPDIR to locate other dotnet processes through
// eventpipe. see: https://github.com/dotnet/diagnostics/blob/master/documentation/design-docs/ipc-protocol.md#transport
//
// In order for diagnostics features to 'find' each other, we need to make $TMPDIR into
// something shared.
//
// see: https://kubernetes.io/docs/tasks/access-application-cluster/communicate-containers-same-pod-shared-volume/
project.EnvironmentVariables.Add(new EnvironmentVariableBuilder("TMPDIR")
{
Value = "/var/tye/diagnostics",
});
foreach (var sidecar in project.Sidecars)
{
sidecar.EnvironmentVariables.Add(new EnvironmentVariableBuilder("TMPDIR")
{
Value = "/var/tye/diagnostics",
});
}
}
var containers = new YamlSequenceNode();
spec.Add("containers", containers);
@ -292,7 +322,7 @@ namespace Microsoft.Tye
project.Bindings.Any(b => b.Protocol == "http" || b.Protocol is null) ||
// We generate environment variables for other services if there dependencies
(bindings is object && bindings.Bindings.Any()))
bindings?.Bindings.Count > 0)
{
var env = new YamlSequenceNode();
container.Add("env", env);
@ -308,88 +338,21 @@ namespace Microsoft.Tye
if (bindings is object)
{
foreach (var binding in bindings.Bindings.OfType<EnvironmentVariableInputBinding>())
{
env.Add(new YamlMappingNode()
{
{ "name", binding.Name },
{ "value", new YamlScalarNode(binding.Value) { Style = ScalarStyle.SingleQuoted, } },
});
}
AddEnvironmentVariablesForComputedBindings(env, bindings);
}
foreach (var binding in bindings.Bindings.OfType<SecretInputBinding>())
{
//- name: SECRET_USERNAME
// valueFrom:
// secretKeyRef:
// name: mysecret
// key: username
if (project.RelocateDiagnosticsDomainSockets)
{
// volumeMounts:
// - name: shared-data
// mountPath: /usr/share/nginx/html
var volumeMounts = new YamlSequenceNode();
container.Add("volumeMounts", volumeMounts);
if (binding is SecretConnectionStringInputBinding connectionStringBinding)
{
env.Add(new YamlMappingNode()
{
{ "name", connectionStringBinding.KeyName },
{ "valueFrom", new YamlMappingNode()
{
{ "secretKeyRef", new YamlMappingNode()
{
{ "name", new YamlScalarNode(binding.Name) { Style = ScalarStyle.SingleQuoted } },
{ "key", new YamlScalarNode("connectionstring") { Style = ScalarStyle.SingleQuoted } },
}
},
}
},
});
}
else if (binding is SecretUrlInputBinding urlBinding)
{
env.Add(new YamlMappingNode()
{
{ "name", $"{urlBinding.KeyNameBase}__PROTOCOL" },
{ "valueFrom", new YamlMappingNode()
{
{ "secretKeyRef", new YamlMappingNode()
{
{ "name", new YamlScalarNode(binding.Name) { Style = ScalarStyle.SingleQuoted } },
{ "key", new YamlScalarNode("protocol") { Style = ScalarStyle.SingleQuoted } },
}
},
}
},
});
env.Add(new YamlMappingNode()
{
{ "name", $"{urlBinding.KeyNameBase}__HOST" },
{ "valueFrom", new YamlMappingNode()
{
{ "secretKeyRef", new YamlMappingNode()
{
{ "name", new YamlScalarNode(binding.Name) { Style = ScalarStyle.SingleQuoted } },
{ "key", new YamlScalarNode("host") { Style = ScalarStyle.SingleQuoted } },
}
},
}
},
});
env.Add(new YamlMappingNode()
{
{ "name", $"{urlBinding.KeyNameBase}__PORT" },
{ "valueFrom", new YamlMappingNode()
{
{ "secretKeyRef", new YamlMappingNode()
{
{ "name", new YamlScalarNode(binding.Name) { Style = ScalarStyle.SingleQuoted } },
{ "key", new YamlScalarNode("port") { Style = ScalarStyle.SingleQuoted } },
}
},
}
},
});
}
}
var volumeMount = new YamlMappingNode();
volumeMounts.Add(volumeMount);
volumeMount.Add("name", "tye-diagnostics");
volumeMount.Add("mountPath", "/var/tye/diagnostics");
}
}
@ -417,7 +380,129 @@ namespace Microsoft.Tye
}
}
foreach (var sidecar in project.Sidecars)
{
var container = new YamlMappingNode();
containers.Add(container);
container.Add("name", sidecar.Name); // NOTE: to really support multiple images we'd need to generate unique names.
container.Add("image", $"{sidecar.ImageName}:{sidecar.ImageTag}");
container.Add("imagePullPolicy", "Always"); // helps avoid problems with development + weak versioning
if (sidecar.Args.Count > 0)
{
var args = new YamlSequenceNode();
container.Add("args", args);
foreach (var arg in sidecar.Args)
{
args.Add(new YamlScalarNode(arg) { Style = ScalarStyle.SingleQuoted, });
}
}
var sidecarBindings = sidecar.Outputs.OfType<ComputedBindings>().FirstOrDefault();
if (sidecar.EnvironmentVariables.Count > 0 || sidecarBindings?.Bindings.Count > 0)
{
var env = new YamlSequenceNode();
container.Add("env", env);
foreach (var kvp in sidecar.EnvironmentVariables)
{
env.Add(new YamlMappingNode()
{
{ "name", kvp.Name },
{ "value", new YamlScalarNode(kvp.Value) { Style = ScalarStyle.SingleQuoted, } },
});
}
if (sidecarBindings is object)
{
AddEnvironmentVariablesForComputedBindings(env, sidecarBindings);
}
}
if (project.RelocateDiagnosticsDomainSockets)
{
// volumeMounts:
// - name: shared-data
// mountPath: /usr/share/nginx/html
var volumeMounts = new YamlSequenceNode();
container.Add("volumeMounts", volumeMounts);
var volumeMount = new YamlMappingNode();
volumeMounts.Add(volumeMount);
volumeMount.Add("name", "tye-diagnostics");
volumeMount.Add("mountPath", "/var/tye/diagnostics");
}
}
if (project.RelocateDiagnosticsDomainSockets)
{
// volumes:
// - name: shared-data
// emptyDir: {}
var volumes = new YamlSequenceNode();
spec.Add("volumes", volumes);
var volume = new YamlMappingNode();
volumes.Add(volume);
volume.Add("name", "tye-diagnostics");
volume.Add("emptyDir", new YamlMappingNode());
}
return new KubernetesDeploymentOutput(project.Name, new YamlDocument(root));
}
private static void AddEnvironmentVariablesForComputedBindings(YamlSequenceNode env, ComputedBindings bindings)
{
foreach (var binding in bindings.Bindings.OfType<EnvironmentVariableInputBinding>())
{
env.Add(new YamlMappingNode()
{
{ "name", binding.Name },
{ "value", new YamlScalarNode(binding.Value) { Style = ScalarStyle.SingleQuoted, } },
});
}
foreach (var binding in bindings.Bindings.OfType<SecretInputBinding>())
{
//- name: SECRET_USERNAME
// valueFrom:
// secretKeyRef:
// name: mysecret
// key: username
if (binding is SecretConnectionStringInputBinding connectionStringBinding)
{
AddSecret(env, connectionStringBinding.KeyName, binding.Name, "connectionstring");
}
else if (binding is SecretUrlInputBinding urlBinding)
{
AddSecret(env, $"{urlBinding.KeyNameBase}__PROTOCOL", binding.Name, "protocol");
AddSecret(env, $"{urlBinding.KeyNameBase}__HOST", binding.Name, "host");
AddSecret(env, $"{urlBinding.KeyNameBase}__PORT", binding.Name, "port");
}
}
static void AddSecret(YamlSequenceNode env, string name, string secret, string key)
{
env.Add(new YamlMappingNode()
{
{ "name", name },
{
"valueFrom", new YamlMappingNode()
{
{
"secretKeyRef", new YamlMappingNode()
{
{ "name", new YamlScalarNode(secret) { Style = ScalarStyle.SingleQuoted } },
{ "key", new YamlScalarNode(key) { Style = ScalarStyle.SingleQuoted } },
}
},
}
},
});
}
}
}
}

Просмотреть файл

@ -38,6 +38,7 @@ namespace Microsoft.Tye
public string PublishDir { get; set; } = default!;
public string IntermediateOutputPath { get; set; } = default!;
public bool IsAspNet { get; set; }
public bool RelocateDiagnosticsDomainSockets { get; set; }
// Data used for building containers
public ContainerInfo? ContainerInfo { get; set; }
@ -51,5 +52,7 @@ namespace Microsoft.Tye
public List<VolumeBuilder> Volumes { get; } = new List<VolumeBuilder>();
public Dictionary<string, string> BuildProperties { get; } = new Dictionary<string, string>();
public List<SidecarBuilder> Sidecars { get; } = new List<SidecarBuilder>();
}
}

Просмотреть файл

@ -0,0 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
namespace Microsoft.Tye
{
public class SidecarBuilder
{
public SidecarBuilder(string name, string imageName, string imageTag)
{
Name = name;
ImageName = imageName;
ImageTag = imageTag;
}
public string Name { get; set; }
public string ImageName { get; set; }
public string ImageTag { get; set; }
public List<string> Args { get; } = new List<string>();
public HashSet<string> Dependencies { get; } = new HashSet<string>();
public List<ServiceOutput> Outputs { get; } = new List<ServiceOutput>();
public List<EnvironmentVariableBuilder> EnvironmentVariables { get; } = new List<EnvironmentVariableBuilder>();
}
}

Просмотреть файл

@ -14,50 +14,75 @@ namespace Microsoft.Tye.Extensions.Zipkin
{
public override Task ProcessAsync(ExtensionContext context, ExtensionConfiguration config)
{
if (context.Operation == ExtensionContext.OperationKind.LocalRun)
if (context.Application.Services.Any(s => s.Name == "zipkin"))
{
if (context.Application.Services.Any(s => s.Name == "zipkin"))
context.Output.WriteDebugLine("zipkin service already configured. Skipping...");
}
else
{
context.Output.WriteDebugLine("Injecting zipkin service...");
var service = new ContainerServiceBuilder("zipkin", "openzipkin/zipkin")
{
context.Output.WriteDebugLine("zipkin service already configured. Skipping...");
}
else
{
context.Output.WriteDebugLine("Injecting zipkin service...");
var service = new ContainerServiceBuilder("zipkin", "openzipkin/zipkin")
Bindings =
{
Bindings =
new BindingBuilder()
{
new BindingBuilder()
{
Port = 9411,
ContainerPort = 9411,
Protocol = "http",
},
Port = 9411,
ContainerPort = 9411,
Protocol = "http",
},
};
context.Application.Services.Add(service);
},
};
context.Application.Services.Add(service);
foreach (var s in context.Application.Services)
foreach (var s in context.Application.Services)
{
if (object.ReferenceEquals(s, service))
{
if (object.ReferenceEquals(s, service))
{
continue;
}
continue;
}
// make zipkin available as a dependency of everything.
if (!s.Dependencies.Contains(service.Name))
{
s.Dependencies.Add(service.Name);
}
// make zipkin available as a dependency of everything.
if (!s.Dependencies.Contains(service.Name))
{
s.Dependencies.Add(service.Name);
}
}
}
if (context.Operation == ExtensionContext.OperationKind.LocalRun)
{
if (context.Options!.DistributedTraceProvider is null)
{
// For local development we hardcode the port and hostname
context.Options.DistributedTraceProvider = "zipkin=http://localhost:9411";
}
}
else if (context.Operation == ExtensionContext.OperationKind.Deploy)
{
foreach (var project in context.Application.Services.OfType<ProjectServiceBuilder>())
{
// Bring your rain boots.
project.RelocateDiagnosticsDomainSockets = true;
var sidecar = new SidecarBuilder("tye-diag-agent", "rynowak/tye-diag-agent", "0.1")
{
Args =
{
"--kubernetes=true",
"--provider:0=zipkin=service:zipkin",
$"--service={project.Name}",
$"--assemblyName={project.AssemblyName}",
},
Dependencies =
{
// Inject the zipkin service discovery variables
"zipkin",
},
};
project.Sidecars.Add(sidecar);
}
}
return Task.CompletedTask;
}

Просмотреть файл

@ -75,7 +75,7 @@ namespace Microsoft.Tye.Hosting.Diagnostics
{
argsJson = exceptionJson;
exceptionJson = eventName;
eventName = null;
eventName = null!;
}
if (string.IsNullOrEmpty(argsJson))

Просмотреть файл

@ -0,0 +1,161 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Tye.Hosting.Diagnostics;
namespace Microsoft.Tye
{
public class DiagnosticsMonitor : BackgroundService
{
private readonly ILogger<DiagnosticsMonitor> logger;
private readonly IOptions<DiagnosticsMonitorOptions> options;
public DiagnosticsMonitor(ILogger<DiagnosticsMonitor> logger, IOptions<DiagnosticsMonitorOptions> options)
{
this.logger = logger;
this.options = options;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var collector = InitializeCollector();
var replicaInfo = new ReplicaInfo(
selector: processes =>
{
// Find process by looking for the entry point dll in the arguments.
return processes.FirstOrDefault(p =>
{
logger.LogDebug("Checking process {PID}.", p.Id);
var command = GetCommand(p);
logger.LogDebug("Searching command '{Command}'.", command);
return command.Contains($"{options.Value.AssemblyName}.dll");
});
},
assemblyName: options.Value.AssemblyName,
service: options.Value.Service,
replica: options.Value.Kubernetes ? Dns.GetHostName() : options.Value.Service,
metrics: new ConcurrentDictionary<string, string>());
while (!stoppingToken.IsCancellationRequested)
{
// The collector has a timeout in waiting for its filter to pass, so we
// won't burn the CPU to a crisp by doing this repetatively.
try
{
logger.LogInformation("Starting data collection");
await collector.CollectAsync(replicaInfo, stoppingToken);
}
catch (Exception ex) when (!stoppingToken.IsCancellationRequested)
{
logger.LogError(ex, "Data collection threw an exception.");
}
}
}
private DiagnosticsCollector InitializeCollector()
{
var collector = new DiagnosticsCollector(this.logger)
{
SelectProcessTimeout = TimeSpan.FromSeconds(60),
};
foreach (var provider in options.Value.Providers)
{
if (!DiagnosticsProvider.WellKnownProviders.TryGetValue(provider.Key, out var wellKnown))
{
logger.LogError("Unknown provider type {Provider}. Skipping.", provider.Value);
continue;
}
switch (wellKnown.Kind)
{
case DiagnosticsProvider.ProviderKind.Logging:
{
if (collector.LoggingSink is object)
{
logger.LogError("Logging is already initialized. Skipping.");
continue;
}
logger.LogInformation(wellKnown.LogFormat, provider.Key);
collector.LoggingSink = new LoggingSink(logger, provider);
break;
}
case DiagnosticsProvider.ProviderKind.Metrics:
{
if (collector.MetricSink is object)
{
logger.LogError("Metrics is already initialized. Skipping.");
continue;
}
// TODO metrics
break;
}
case DiagnosticsProvider.ProviderKind.Tracing:
{
if (collector.TracingSink is object)
{
logger.LogError("Tracing is already initialized. Skipping.");
continue;
}
logger.LogInformation(wellKnown.LogFormat, provider.Value);
collector.TracingSink = new TracingSink(logger, provider);
break;
}
default:
logger.LogError("Unknown provider type. Skipping.");
break;
}
}
return collector;
}
private static string GetCommand(Process target)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
throw new PlatformNotSupportedException();
}
using var process = new Process()
{
StartInfo =
{
FileName = "ps",
Arguments = $"-p {target.Id.ToString(CultureInfo.InvariantCulture)} -o command=",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
},
EnableRaisingEvents = true,
};
process.Start();
process.WaitForExit();
return process.StandardOutput.ReadToEnd().Trim();
}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
namespace Microsoft.Tye
{
public class DiagnosticsMonitorOptions
{
public bool Kubernetes { get; set; }
public string Service { get; set; } = default!;
public string AssemblyName { get; set; } = default!;
public List<DiagnosticsProvider> Providers { get; } = new List<DiagnosticsProvider>();
}
}

Просмотреть файл

@ -0,0 +1,10 @@
FROM mcr.microsoft.com/dotnet/core/runtime:3.1
WORKDIR /app
# We need `ps`
RUN apt-get update \
&& apt-get install -y procps \
&& rm -rf /var/lib/apt/lists/*
COPY . .
ENTRYPOINT ["dotnet", "tye-diag-agent.dll"]

Просмотреть файл

@ -0,0 +1,67 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.Tye
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
var configuration = hostContext.Configuration;
services.Configure<DiagnosticsMonitorOptions>(options =>
{
options.Kubernetes = string.Equals(bool.TrueString, configuration["kubernetes"], StringComparison.OrdinalIgnoreCase);
options.AssemblyName = configuration["assemblyName"];
options.Service = configuration["service"];
var section = configuration.GetSection("provider");
foreach (var child in section.GetChildren())
{
if (!DiagnosticsProvider.TryParse(child.Value, DiagnosticsProvider.ProviderKind.Unknown, out var provider))
{
throw new InvalidOperationException("Could not parse diagnostics provider: " + child.Value);
}
// The value used to connect to the service should be accessible with Tye service discovery.
if (provider.Value is string && provider.Value.StartsWith("service:"))
{
var service = provider.Value.Substring("service:".Length);
var value = configuration.GetServiceUri(service)?.AbsoluteUri ?? configuration.GetConnectionString(service);
provider = new DiagnosticsProvider(provider.Key, value, provider.Kind);
}
options.Providers.Add(provider);
}
if (options.Providers.Count == 0)
{
throw new InvalidOperationException("At least one provider must be configured.");
}
if (string.IsNullOrEmpty(options.AssemblyName))
{
throw new InvalidOperationException("The assembly name is required.");
}
if (string.IsNullOrEmpty(options.Service))
{
throw new InvalidOperationException("The service name is required.");
}
});
services.AddHostedService<DiagnosticsMonitor>();
});
}
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"profiles": {
"tye_diag_agent": {
"commandName": "Project",
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Tye": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Tye": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

27
src/tye-diag-agent/docker.sh Executable file
Просмотреть файл

@ -0,0 +1,27 @@
#!/usr/bin/env bash
source="${BASH_SOURCE[0]}"
# resolve $SOURCE until the file is no longer a symlink
while [[ -h $source ]]; do
scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
source="$(readlink "$source")"
# if $source was a relative symlink, we need to resolve it relative to the path where the
# symlink file was located
[[ $source != /* ]] && source="$scriptroot/$source"
done
scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
rm -rf "$scriptroot/../../artifacts/tye-diag-agent-publish"
dotnet publish "$scriptroot" \
-c Release \
-o "$scriptroot/../../artifacts/tye-diag-agent-publish" \
-r linux-x64 --self-contained false
if [ -z "$1" ]; then
docker build "$scriptroot/../../artifacts/tye-diag-agent-publish" -f "$scriptroot/Dockerfile"
else
docker build "$scriptroot/../../artifacts/tye-diag-agent-publish" -f "$scriptroot/Dockerfile" -t "rynowak/tye-diag-agent:$1"
fi

Просмотреть файл

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Microsoft.Tye</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Tye.Extensions.Configuration\Microsoft.Tye.Extensions.Configuration.csproj" />
<ProjectReference Include="..\Microsoft.Tye.Hosting.Diagnostics\Microsoft.Tye.Hosting.Diagnostics.csproj" />
</ItemGroup>
</Project>

15
tye.sln
Просмотреть файл

@ -31,6 +31,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Tye.Extensions.Co
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.Infrastructure", "test\Test.Infrastructure\Test.Infrastructure.csproj", "{F9A7C801-26F3-4D20-B084-2A6A6343E869}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tye-diag-agent", "src\tye-diag-agent\tye-diag-agent.csproj", "{7173D2B6-B102-4DCA-8000-5DAE684BFC13}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -185,6 +187,18 @@ Global
{F9A7C801-26F3-4D20-B084-2A6A6343E869}.Release|x64.Build.0 = Release|Any CPU
{F9A7C801-26F3-4D20-B084-2A6A6343E869}.Release|x86.ActiveCfg = Release|Any CPU
{F9A7C801-26F3-4D20-B084-2A6A6343E869}.Release|x86.Build.0 = Release|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Debug|x64.ActiveCfg = Debug|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Debug|x64.Build.0 = Debug|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Debug|x86.ActiveCfg = Debug|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Debug|x86.Build.0 = Debug|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Release|Any CPU.Build.0 = Release|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Release|x64.ActiveCfg = Release|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Release|x64.Build.0 = Release|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Release|x86.ActiveCfg = Release|Any CPU
{7173D2B6-B102-4DCA-8000-5DAE684BFC13}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -202,6 +216,7 @@ Global
{7C9021B7-64BA-4DA9-88DA-5BC12A1C6233} = {8C662D59-A3CB-466F-8E85-A8E6BA5E7601}
{FCE7C889-16D1-42E7-A514-EA096E9D41A7} = {F19B02EB-A372-417A-B2C2-EA0D5A3C76D5}
{F9A7C801-26F3-4D20-B084-2A6A6343E869} = {F19B02EB-A372-417A-B2C2-EA0D5A3C76D5}
{7173D2B6-B102-4DCA-8000-5DAE684BFC13} = {8C662D59-A3CB-466F-8E85-A8E6BA5E7601}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D8002603-BB27-4500-BF86-274A8E72D302}