mirror of https://github.com/dotnet/tye.git
committed by
GitHub
33 changed files with 700 additions and 95 deletions
@ -0,0 +1,6 @@ |
|||
# tye application configuration file |
|||
# read all about it at https://github.com/dotnet/tye |
|||
name: single-project |
|||
services: |
|||
- name: test-project |
|||
project: test-project/test-project.csproj |
|||
@ -0,0 +1,59 @@ |
|||
// 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; |
|||
using System.CommandLine; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Tye.ConfigModel; |
|||
|
|||
namespace Tye |
|||
{ |
|||
public class GenerateHost |
|||
{ |
|||
public static Task GenerateAsync(IConsole console, FileInfo path, Verbosity verbosity, bool interactive) |
|||
{ |
|||
var application = ConfigFactory.FromFile(path); |
|||
return ExecuteGenerateAsync(new OutputContext(console, verbosity), application, environment: "production", interactive); |
|||
} |
|||
|
|||
public static async Task ExecuteGenerateAsync(OutputContext output, ConfigApplication application, string environment, bool interactive) |
|||
{ |
|||
var temporaryApplication = await Program.CreateApplicationAdapterAsync(output, application, interactive); |
|||
var steps = new List<ServiceExecutor.Step>() |
|||
{ |
|||
new CombineStep() { Environment = environment, }, |
|||
new PublishProjectStep(), |
|||
new BuildDockerImageStep() { Environment = environment, }, // Make an image but don't push it
|
|||
}; |
|||
|
|||
steps.Add(new GenerateKubernetesManifestStep() { Environment = environment, }); |
|||
|
|||
var executor = new ServiceExecutor(output, temporaryApplication, steps); |
|||
foreach (var service in temporaryApplication.Services) |
|||
{ |
|||
await executor.ExecuteAsync(service); |
|||
} |
|||
|
|||
await GenerateApplicationManifestAsync(output, temporaryApplication, application.Name ?? application.Source.Directory.Name, environment); |
|||
} |
|||
|
|||
private static async Task GenerateApplicationManifestAsync(OutputContext output, Tye.Application application, string applicationName, string environment) |
|||
{ |
|||
using var step = output.BeginStep("Generating Application Manifests..."); |
|||
|
|||
var outputFilePath = Path.GetFullPath(Path.Combine(application.RootDirectory, $"{applicationName}-generate-{environment}.yaml")); |
|||
output.WriteInfoLine($"Writing output to '{outputFilePath}'."); |
|||
{ |
|||
using var stream = File.OpenWrite(outputFilePath); |
|||
using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true); |
|||
|
|||
await ApplicationYamlWriter.WriteAsync(output, writer, application); |
|||
} |
|||
|
|||
step.MarkComplete(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
using Xunit; |
|||
using Xunit.Sdk; |
|||
|
|||
namespace E2ETest |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] |
|||
[XunitTestCaseDiscoverer("E2ETest." + nameof(ConditionalFactDiscoverer), "E2ETest")] |
|||
public class ConditionalFactAttribute : FactAttribute |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using Xunit.Abstractions; |
|||
using Xunit.Sdk; |
|||
|
|||
// Do not change this namespace without changing the usage in ConditionalFactAttribute
|
|||
namespace E2ETest |
|||
{ |
|||
internal class ConditionalFactDiscoverer : FactDiscoverer |
|||
{ |
|||
private readonly IMessageSink _diagnosticMessageSink; |
|||
|
|||
public ConditionalFactDiscoverer(IMessageSink diagnosticMessageSink) |
|||
: base(diagnosticMessageSink) |
|||
{ |
|||
_diagnosticMessageSink = diagnosticMessageSink; |
|||
} |
|||
|
|||
protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute) |
|||
{ |
|||
var skipReason = testMethod.EvaluateSkipConditions(); |
|||
return skipReason != null |
|||
? new SkippedTestCase(skipReason, _diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), TestMethodDisplayOptions.None, testMethod) |
|||
: base.CreateTestCase(discoveryOptions, testMethod, factAttribute); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace E2ETest |
|||
{ |
|||
public interface ITestCondition |
|||
{ |
|||
bool IsMet { get; } |
|||
|
|||
string SkipReason { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
// 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 Tye; |
|||
|
|||
namespace E2ETest |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] |
|||
internal class SkipIfDockerNotRunningAttribute : Attribute, ITestCondition |
|||
{ |
|||
public SkipIfDockerNotRunningAttribute() |
|||
{ |
|||
// TODO Check performance of this.
|
|||
IsMet = DockerDetector.Instance.IsDockerConnectedToDaemon.Value.GetAwaiter().GetResult(); |
|||
SkipReason = "Docker is not installed or running."; |
|||
} |
|||
|
|||
public bool IsMet { get; } |
|||
|
|||
public string SkipReason { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using System; |
|||
using Xunit.Abstractions; |
|||
using Xunit.Sdk; |
|||
|
|||
namespace E2ETest |
|||
{ |
|||
public class SkippedTestCase : XunitTestCase |
|||
{ |
|||
private string _skipReason; |
|||
|
|||
[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] |
|||
public SkippedTestCase() : base() |
|||
{ |
|||
_skipReason = ""; |
|||
} |
|||
|
|||
public SkippedTestCase( |
|||
string skipReason, |
|||
IMessageSink diagnosticMessageSink, |
|||
TestMethodDisplay defaultMethodDisplay, |
|||
TestMethodDisplayOptions defaultMethodDisplayOptions, |
|||
ITestMethod testMethod) |
|||
: base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod) |
|||
{ |
|||
_skipReason = skipReason; |
|||
} |
|||
|
|||
protected override string GetSkipReason(IAttributeInfo factAttribute) |
|||
=> _skipReason ?? base.GetSkipReason(factAttribute); |
|||
|
|||
public override void Deserialize(IXunitSerializationInfo data) |
|||
{ |
|||
_skipReason = data.GetValue<string>(nameof(_skipReason)); |
|||
|
|||
// We need to call base after reading our value, because Deserialize will call
|
|||
// into GetSkipReason.
|
|||
base.Deserialize(data); |
|||
} |
|||
|
|||
public override void Serialize(IXunitSerializationInfo data) |
|||
{ |
|||
base.Serialize(data); |
|||
data.AddValue(nameof(_skipReason), _skipReason); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System.Linq; |
|||
using Xunit.Abstractions; |
|||
using Xunit.Sdk; |
|||
|
|||
namespace E2ETest |
|||
{ |
|||
public static class TestMethodExtensions |
|||
{ |
|||
public static string EvaluateSkipConditions(this ITestMethod testMethod) |
|||
{ |
|||
var testClass = testMethod.TestClass.Class; |
|||
var assembly = testMethod.TestClass.TestCollection.TestAssembly.Assembly; |
|||
var conditionAttributes = testMethod.Method |
|||
.GetCustomAttributes(typeof(ITestCondition)) |
|||
.Concat(testClass.GetCustomAttributes(typeof(ITestCondition))) |
|||
.Concat(assembly.GetCustomAttributes(typeof(ITestCondition))) |
|||
.OfType<ReflectionAttributeInfo>() |
|||
.Select(attributeInfo => attributeInfo.Attribute); |
|||
|
|||
foreach (ITestCondition condition in conditionAttributes) |
|||
{ |
|||
if (!condition.IsMet) |
|||
{ |
|||
return condition.SkipReason; |
|||
} |
|||
} |
|||
|
|||
return null!; |
|||
} |
|||
} |
|||
} |
|||
@ -1,20 +1,38 @@ |
|||
using Serilog.Core; |
|||
using System.CommandLine; |
|||
using System.CommandLine.IO; |
|||
using Serilog.Core; |
|||
using Serilog.Events; |
|||
using Xunit.Abstractions; |
|||
|
|||
namespace E2ETest |
|||
{ |
|||
public class TestOutputLogEventSink : ILogEventSink |
|||
public class TestOutputLogEventSink : ILogEventSink, IConsole, IStandardStreamWriter |
|||
{ |
|||
private readonly ITestOutputHelper output; |
|||
|
|||
public TestOutputLogEventSink(ITestOutputHelper output) |
|||
{ |
|||
this.output = output; |
|||
} |
|||
|
|||
public IStandardStreamWriter Out => this; |
|||
|
|||
public bool IsOutputRedirected => false; |
|||
|
|||
public IStandardStreamWriter Error => this; |
|||
|
|||
public bool IsErrorRedirected => false; |
|||
|
|||
public bool IsInputRedirected => false; |
|||
|
|||
public void Emit(LogEvent logEvent) |
|||
{ |
|||
output.WriteLine(logEvent.RenderMessage()); |
|||
} |
|||
|
|||
public void Write(string value) |
|||
{ |
|||
output.WriteLine(value); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,106 @@ |
|||
// 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.IO; |
|||
using System.Threading.Tasks; |
|||
using Tye; |
|||
using Tye.ConfigModel; |
|||
using Xunit; |
|||
using Xunit.Abstractions; |
|||
|
|||
namespace E2ETest |
|||
{ |
|||
public class TyeGenerateTests |
|||
{ |
|||
private readonly ITestOutputHelper output; |
|||
private readonly TestOutputLogEventSink sink; |
|||
|
|||
public TyeGenerateTests(ITestOutputHelper output) |
|||
{ |
|||
this.output = output; |
|||
sink = new TestOutputLogEventSink(output); |
|||
} |
|||
|
|||
[ConditionalFact] |
|||
[SkipIfDockerNotRunning] |
|||
public async Task SingleProjectGenerateTest() |
|||
{ |
|||
var projectName = "single-project"; |
|||
var environment = "production"; |
|||
|
|||
var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", projectName)); |
|||
using var tempDirectory = TempDirectory.Create(); |
|||
DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); |
|||
|
|||
var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); |
|||
|
|||
var application = ConfigFactory.FromFile(projectFile); |
|||
|
|||
// Need to add docker registry for generate
|
|||
application.Registry = "test"; |
|||
|
|||
await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); |
|||
|
|||
// name of application is the folder
|
|||
var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); |
|||
var expectedContent = File.ReadAllText($"testassets/generate/{projectName}.yaml"); |
|||
|
|||
Assert.Equal(expectedContent, content); |
|||
} |
|||
|
|||
[ConditionalFact] |
|||
[SkipIfDockerNotRunning] |
|||
public async Task FrontendBackendGenerateTest() |
|||
{ |
|||
var projectName = "frontend-backend"; |
|||
var environment = "production"; |
|||
|
|||
var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", projectName)); |
|||
using var tempDirectory = TempDirectory.Create(); |
|||
DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); |
|||
|
|||
var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); |
|||
|
|||
var application = ConfigFactory.FromFile(projectFile); |
|||
|
|||
// Need to add docker registry for generate
|
|||
application.Registry = "test"; |
|||
|
|||
await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); |
|||
|
|||
// name of application is the folder
|
|||
var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); |
|||
var expectedContent = File.ReadAllText($"testassets/generate/{projectName}.yaml"); |
|||
|
|||
Assert.Equal(expectedContent, content); |
|||
} |
|||
|
|||
[ConditionalFact] |
|||
[SkipIfDockerNotRunning] |
|||
public async Task MultipleProjectGenerateTest() |
|||
{ |
|||
var projectName = "multi-project"; |
|||
var environment = "production"; |
|||
|
|||
var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", projectName)); |
|||
using var tempDirectory = TempDirectory.Create(); |
|||
DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); |
|||
|
|||
var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); |
|||
|
|||
var application = ConfigFactory.FromFile(projectFile); |
|||
|
|||
// Need to add docker registry for generate
|
|||
application.Registry = "test"; |
|||
|
|||
await GenerateHost.ExecuteGenerateAsync(new OutputContext(sink, Verbosity.Debug), application, environment, interactive: false); |
|||
|
|||
// name of application is the folder
|
|||
var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); |
|||
var expectedContent = File.ReadAllText($"testassets/generate/{projectName}.yaml"); |
|||
|
|||
Assert.Equal(expectedContent, content); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
kind: Deployment |
|||
apiVersion: apps/v1 |
|||
metadata: |
|||
name: backend |
|||
labels: |
|||
app.kubernetes.io/name: backend |
|||
app.kubernetes.io/part-of: frontend-backend |
|||
spec: |
|||
replicas: 1 |
|||
selector: |
|||
matchLabels: |
|||
app.kubernetes.io/name: backend |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app.kubernetes.io/name: backend |
|||
app.kubernetes.io/part-of: frontend-backend |
|||
spec: |
|||
containers: |
|||
- name: backend |
|||
image: test/backend:1.0.0 |
|||
imagePullPolicy: Always |
|||
env: |
|||
- name: ASPNETCORE_URLS |
|||
value: http://*:5050 |
|||
- name: SERVICE__FRONTEND__PORT |
|||
value: '5051' |
|||
- name: SERVICE__FRONTEND__HOST |
|||
value: 'frontend' |
|||
ports: |
|||
- containerPort: 5050 |
|||
... |
|||
--- |
|||
kind: Service |
|||
apiVersion: v1 |
|||
metadata: |
|||
name: backend |
|||
labels: |
|||
app.kubernetes.io/name: backend |
|||
app.kubernetes.io/part-of: frontend-backend |
|||
spec: |
|||
selector: |
|||
app.kubernetes.io/name: backend |
|||
type: ClusterIP |
|||
ports: |
|||
- name: backend |
|||
protocol: TCP |
|||
port: 5050 |
|||
targetPort: 5050 |
|||
... |
|||
--- |
|||
kind: Deployment |
|||
apiVersion: apps/v1 |
|||
metadata: |
|||
name: frontend |
|||
labels: |
|||
app.kubernetes.io/name: frontend |
|||
app.kubernetes.io/part-of: frontend-backend |
|||
spec: |
|||
replicas: 1 |
|||
selector: |
|||
matchLabels: |
|||
app.kubernetes.io/name: frontend |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app.kubernetes.io/name: frontend |
|||
app.kubernetes.io/part-of: frontend-backend |
|||
spec: |
|||
containers: |
|||
- name: frontend |
|||
image: test/frontend:1.0.0 |
|||
imagePullPolicy: Always |
|||
env: |
|||
- name: ASPNETCORE_URLS |
|||
value: http://*:5051 |
|||
- name: SERVICE__BACKEND__PORT |
|||
value: '5050' |
|||
- name: SERVICE__BACKEND__HOST |
|||
value: 'backend' |
|||
ports: |
|||
- containerPort: 5051 |
|||
... |
|||
--- |
|||
kind: Service |
|||
apiVersion: v1 |
|||
metadata: |
|||
name: frontend |
|||
labels: |
|||
app.kubernetes.io/name: frontend |
|||
app.kubernetes.io/part-of: frontend-backend |
|||
spec: |
|||
selector: |
|||
app.kubernetes.io/name: frontend |
|||
type: ClusterIP |
|||
ports: |
|||
- name: frontend |
|||
protocol: TCP |
|||
port: 5051 |
|||
targetPort: 5051 |
|||
... |
|||
@ -0,0 +1,167 @@ |
|||
kind: Deployment |
|||
apiVersion: apps/v1 |
|||
metadata: |
|||
name: backend |
|||
labels: |
|||
app.kubernetes.io/name: backend |
|||
app.kubernetes.io/part-of: multi-project |
|||
spec: |
|||
replicas: 1 |
|||
selector: |
|||
matchLabels: |
|||
app.kubernetes.io/name: backend |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app.kubernetes.io/name: backend |
|||
app.kubernetes.io/part-of: multi-project |
|||
spec: |
|||
containers: |
|||
- name: backend |
|||
image: test/backend:1.0.0 |
|||
imagePullPolicy: Always |
|||
env: |
|||
- name: ASPNETCORE_URLS |
|||
value: http://*:7000 |
|||
- name: SERVICE__FRONTEND__PORT |
|||
value: '8000' |
|||
- name: SERVICE__FRONTEND__HOST |
|||
value: 'frontend' |
|||
volumeMounts: |
|||
- name: rabbit-rabbit |
|||
mountPath: /var/tye/bindings/rabbit-rabbit |
|||
readOnly: true |
|||
ports: |
|||
- containerPort: 7000 |
|||
volumes: |
|||
- name: rabbit-rabbit |
|||
secret: |
|||
secretName: binding-production-rabbit-rabbit-secret |
|||
items: |
|||
- key: connectionstring |
|||
path: CONNECTIONSTRING__RABBIT |
|||
... |
|||
--- |
|||
kind: Service |
|||
apiVersion: v1 |
|||
metadata: |
|||
name: backend |
|||
labels: |
|||
app.kubernetes.io/name: backend |
|||
app.kubernetes.io/part-of: multi-project |
|||
spec: |
|||
selector: |
|||
app.kubernetes.io/name: backend |
|||
type: ClusterIP |
|||
ports: |
|||
- name: backend |
|||
protocol: TCP |
|||
port: 7000 |
|||
targetPort: 7000 |
|||
... |
|||
--- |
|||
kind: Deployment |
|||
apiVersion: apps/v1 |
|||
metadata: |
|||
name: frontend |
|||
labels: |
|||
app.kubernetes.io/name: frontend |
|||
app.kubernetes.io/part-of: multi-project |
|||
spec: |
|||
replicas: 2 |
|||
selector: |
|||
matchLabels: |
|||
app.kubernetes.io/name: frontend |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app.kubernetes.io/name: frontend |
|||
app.kubernetes.io/part-of: multi-project |
|||
spec: |
|||
containers: |
|||
- name: frontend |
|||
image: test/frontend:1.0.0 |
|||
imagePullPolicy: Always |
|||
env: |
|||
- name: ASPNETCORE_URLS |
|||
value: http://*:8000 |
|||
- name: SERVICE__BACKEND__PORT |
|||
value: '7000' |
|||
- name: SERVICE__BACKEND__HOST |
|||
value: 'backend' |
|||
volumeMounts: |
|||
- name: rabbit-rabbit |
|||
mountPath: /var/tye/bindings/rabbit-rabbit |
|||
readOnly: true |
|||
ports: |
|||
- containerPort: 8000 |
|||
volumes: |
|||
- name: rabbit-rabbit |
|||
secret: |
|||
secretName: binding-production-rabbit-rabbit-secret |
|||
items: |
|||
- key: connectionstring |
|||
path: CONNECTIONSTRING__RABBIT |
|||
... |
|||
--- |
|||
kind: Service |
|||
apiVersion: v1 |
|||
metadata: |
|||
name: frontend |
|||
labels: |
|||
app.kubernetes.io/name: frontend |
|||
app.kubernetes.io/part-of: multi-project |
|||
spec: |
|||
selector: |
|||
app.kubernetes.io/name: frontend |
|||
type: ClusterIP |
|||
ports: |
|||
- name: frontend |
|||
protocol: TCP |
|||
port: 8000 |
|||
targetPort: 8000 |
|||
... |
|||
--- |
|||
kind: Deployment |
|||
apiVersion: apps/v1 |
|||
metadata: |
|||
name: worker |
|||
labels: |
|||
app.kubernetes.io/name: worker |
|||
app.kubernetes.io/part-of: multi-project |
|||
spec: |
|||
replicas: 1 |
|||
selector: |
|||
matchLabels: |
|||
app.kubernetes.io/name: worker |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app.kubernetes.io/name: worker |
|||
app.kubernetes.io/part-of: multi-project |
|||
spec: |
|||
containers: |
|||
- name: worker |
|||
image: test/worker:1.0.0 |
|||
imagePullPolicy: Always |
|||
env: |
|||
- name: SERVICE__BACKEND__PORT |
|||
value: '7000' |
|||
- name: SERVICE__BACKEND__HOST |
|||
value: 'backend' |
|||
- name: SERVICE__FRONTEND__PORT |
|||
value: '8000' |
|||
- name: SERVICE__FRONTEND__HOST |
|||
value: 'frontend' |
|||
volumeMounts: |
|||
- name: rabbit-rabbit |
|||
mountPath: /var/tye/bindings/rabbit-rabbit |
|||
readOnly: true |
|||
volumes: |
|||
- name: rabbit-rabbit |
|||
secret: |
|||
secretName: binding-production-rabbit-rabbit-secret |
|||
items: |
|||
- key: connectionstring |
|||
path: CONNECTIONSTRING__RABBIT |
|||
... |
|||
@ -0,0 +1,51 @@ |
|||
kind: Deployment |
|||
apiVersion: apps/v1 |
|||
metadata: |
|||
name: test-project |
|||
labels: |
|||
app.kubernetes.io/name: test-project |
|||
app.kubernetes.io/part-of: single-project |
|||
spec: |
|||
replicas: 1 |
|||
selector: |
|||
matchLabels: |
|||
app.kubernetes.io/name: test-project |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app.kubernetes.io/name: test-project |
|||
app.kubernetes.io/part-of: single-project |
|||
spec: |
|||
containers: |
|||
- name: test-project |
|||
image: test/test-project:1.0.0 |
|||
imagePullPolicy: Always |
|||
env: |
|||
- name: ASPNETCORE_URLS |
|||
value: http://*:5000 |
|||
ports: |
|||
- containerPort: 5001 |
|||
- containerPort: 5000 |
|||
... |
|||
--- |
|||
kind: Service |
|||
apiVersion: v1 |
|||
metadata: |
|||
name: test-project |
|||
labels: |
|||
app.kubernetes.io/name: test-project |
|||
app.kubernetes.io/part-of: single-project |
|||
spec: |
|||
selector: |
|||
app.kubernetes.io/name: test-project |
|||
type: ClusterIP |
|||
ports: |
|||
- name: test-project |
|||
protocol: TCP |
|||
port: 5001 |
|||
targetPort: 5001 |
|||
- name: test-project |
|||
protocol: TCP |
|||
port: 5000 |
|||
targetPort: 5000 |
|||
... |
|||
Loading…
Reference in new issue