diff --git a/samples/eShopLite/AppHost/aspire-manifest.json b/samples/eShopLite/AppHost/aspire-manifest.json index 4ca63363b7..2ae79f31a9 100644 --- a/samples/eShopLite/AppHost/aspire-manifest.json +++ b/samples/eShopLite/AppHost/aspire-manifest.json @@ -32,10 +32,7 @@ } }, "messaging": { - "type": "azure.servicebus.v1", - "queues": [ - "orders" - ] + "type": "rabbitmq.server.v1" }, "basketservice": { "type": "project.v1", diff --git a/src/Aspire.Hosting/ApplicationModel/DcpResourceAnnotation.cs b/src/Aspire.Hosting/ApplicationModel/DcpResourceAnnotation.cs deleted file mode 100644 index 0a9e140bd3..0000000000 --- a/src/Aspire.Hosting/ApplicationModel/DcpResourceAnnotation.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Aspire.Hosting.ApplicationModel; - -public class DcpResourceAnnotation(string resourceNamespace, string resourceName, string resourceKind) : IResourceAnnotation -{ - public string ResourceNamespace { get; } = resourceNamespace ?? ""; - public string ResourceName { get; } = resourceName; - public string ResourceKind { get; } = resourceKind; -} diff --git a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs index be4f627bd3..841c4ca97f 100644 --- a/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs +++ b/src/Aspire.Hosting/Dcp/ApplicationExecutor.cs @@ -404,9 +404,7 @@ private async Task CreateExecutablesAsync(IEnumerable executableRes spec.Env.Add(new EnvVar { Name = c.Key, Value = c.Value }); } - var createdExecutable = await createResource().ConfigureAwait(false); - var dcpResourceAnnotation = new DcpResourceAnnotation(createdExecutable.Metadata.NamespaceProperty, createdExecutable.Metadata.Name, createdExecutable.Kind); - er.ModelResource.Annotations.Add(dcpResourceAnnotation); + await createResource().ConfigureAwait(false); } } @@ -552,9 +550,7 @@ private async Task CreateContainersAsync(IEnumerable containerResou } } - var createdContainer = await kubernetesService.CreateAsync(dcpContainerResource, cancellationToken).ConfigureAwait(false); - var dcpResourceAnnotation = new DcpResourceAnnotation(createdContainer.Metadata.NamespaceProperty, createdContainer.Metadata.Name, createdContainer.Kind); - cr.ModelResource.Annotations.Add(dcpResourceAnnotation); + await kubernetesService.CreateAsync(dcpContainerResource, cancellationToken).ConfigureAwait(false); } } finally diff --git a/src/Aspire.Hosting/Dcp/DcpDistributedApplicationLifecycleHook.cs b/src/Aspire.Hosting/Dcp/DcpDistributedApplicationLifecycleHook.cs index f08ae19579..fb5ee77a87 100644 --- a/src/Aspire.Hosting/Dcp/DcpDistributedApplicationLifecycleHook.cs +++ b/src/Aspire.Hosting/Dcp/DcpDistributedApplicationLifecycleHook.cs @@ -9,7 +9,7 @@ namespace Aspire.Hosting.Dcp; -public sealed class DcpDistributedApplicationLifecycleHook(IOptions publishingOptions) : IDistributedApplicationLifecycleHook +internal sealed class DcpDistributedApplicationLifecycleHook(IOptions publishingOptions) : IDistributedApplicationLifecycleHook { private readonly IOptions _publishingOptions = publishingOptions; diff --git a/src/Aspire.Hosting/Dcp/DcpOptions.cs b/src/Aspire.Hosting/Dcp/DcpOptions.cs index 9a089d5b21..72f4eefd86 100644 --- a/src/Aspire.Hosting/Dcp/DcpOptions.cs +++ b/src/Aspire.Hosting/Dcp/DcpOptions.cs @@ -7,7 +7,7 @@ namespace Aspire.Hosting.Dcp; -public sealed class DcpOptions +internal sealed class DcpOptions { private const string DcpCliPathMetadataKey = "DcpCliPath"; private const string DcpExtensionsPathMetadataKey = "DcpExtensionsPath"; diff --git a/src/Aspire.Hosting/Dcp/KubernetesService.cs b/src/Aspire.Hosting/Dcp/KubernetesService.cs index c3c3b43b62..81c633d2f0 100644 --- a/src/Aspire.Hosting/Dcp/KubernetesService.cs +++ b/src/Aspire.Hosting/Dcp/KubernetesService.cs @@ -9,7 +9,7 @@ namespace Aspire.Hosting.Dcp; -public enum DcpApiOperationType +internal enum DcpApiOperationType { Create = 1, List = 2, diff --git a/src/Aspire.Hosting/Dcp/Model/Container.cs b/src/Aspire.Hosting/Dcp/Model/Container.cs index bb526cc4a9..f5b8662899 100644 --- a/src/Aspire.Hosting/Dcp/Model/Container.cs +++ b/src/Aspire.Hosting/Dcp/Model/Container.cs @@ -7,7 +7,7 @@ namespace Aspire.Hosting.Dcp.Model; -public class ContainerSpec +internal sealed class ContainerSpec { // Image to be used to create the container [JsonPropertyName("image")] @@ -42,16 +42,16 @@ public class ContainerSpec public List? Args { get; set; } } -public static class VolumeMountType +internal static class VolumeMountType { // A volume mount to a host directory - public static readonly string Bind = "bind"; + public const string Bind = "bind"; // A volume mount to a named volume managed by the container orchestrator - public static readonly string Named = "volume"; + public const string Named = "volume"; } -public class VolumeMount +internal sealed class VolumeMount { [JsonPropertyName("type")] public string Type { get; set; } = VolumeMountType.Bind; @@ -70,22 +70,22 @@ public class VolumeMount public bool IsReadOnly { get; set; } = false; } -public static class ContainerRestartPolicy +internal static class ContainerRestartPolicy { // Do not automatically restart the container when it exits (default) - public static readonly string None = "no"; + public const string None = "no"; // Restart only if the container exits with non-zero status - public static readonly string OnFailure = "on-failure"; + public const string OnFailure = "on-failure"; // Restart container, except if container is explicitly stopped (or container daemon is stopped/restarted) - public static readonly string UnlessStopped = "unless-stopped"; + public const string UnlessStopped = "unless-stopped"; // Always try to restart the container - public static readonly string Always = "always"; + public const string Always = "always"; } -public static class PortProtocol +internal static class PortProtocol { public const string TCP = "TCP"; @@ -132,7 +132,7 @@ public static string FromProtocolType(ProtocolType protocolType) } } -public class ContainerPortSpec +internal sealed class ContainerPortSpec { // Optional: If specified, this must be a valid port number, 0 < x < 65536. [JsonPropertyName("hostPort")] @@ -151,7 +151,7 @@ public class ContainerPortSpec public string? HostIP { get; set; } } -public class ContainerStatus : V1Status +internal sealed class ContainerStatus : V1Status { // Current state of the Container. [JsonPropertyName("state")] @@ -178,31 +178,31 @@ public class ContainerStatus : V1Status // It is provided by V1Status base class. } -public static class ContainerState +internal static class ContainerState { // Pending is the initial Container state. No attempt has been made to run the container yet. - public static readonly string Pending = "Pending"; + public const string Pending = "Pending"; // A start attempt was made, but it failed - public static readonly string FailedToStart = "FailedToStart"; + public const string FailedToStart = "FailedToStart"; // Container has been started and is executing - public static readonly string Running = "Running"; + public const string Running = "Running"; // Container is paused - public static readonly string Paused = "Paused"; + public const string Paused = "Paused"; // Container finished execution - public static readonly string Exited = "Exited"; + public const string Exited = "Exited"; // Container was running at some point, but has been removed. - public static readonly string Removed = "Removed"; + public const string Removed = "Removed"; // Unknown means for some reason container state is unavailable. - public static readonly string Unknown = "Unknown"; + public const string Unknown = "Unknown"; } -public class Container : CustomResource +internal sealed class Container : CustomResource { [JsonConstructor] public Container(ContainerSpec spec) : base(spec) { } diff --git a/src/Aspire.Hosting/Dcp/Model/Endpoint.cs b/src/Aspire.Hosting/Dcp/Model/Endpoint.cs index a2618377b6..0179af5d7b 100644 --- a/src/Aspire.Hosting/Dcp/Model/Endpoint.cs +++ b/src/Aspire.Hosting/Dcp/Model/Endpoint.cs @@ -6,7 +6,7 @@ namespace Aspire.Hosting.Dcp.Model; -public class EndpointSpec +internal sealed class EndpointSpec { // Namespace of the service the endpoint implements [JsonPropertyName("serviceNamespace")] @@ -25,12 +25,12 @@ public class EndpointSpec public int? Port { get; set; } } -public class EndpointStatus : V1Status +internal sealed class EndpointStatus : V1Status { // Currently Endpoint has no status properties, but that may change in future. } -public class Endpoint : CustomResource +internal sealed class Endpoint : CustomResource { [JsonConstructor] public Endpoint(EndpointSpec spec) : base(spec) { } diff --git a/src/Aspire.Hosting/Dcp/Model/Executable.cs b/src/Aspire.Hosting/Dcp/Model/Executable.cs index d449fa541b..3a5ff4250b 100644 --- a/src/Aspire.Hosting/Dcp/Model/Executable.cs +++ b/src/Aspire.Hosting/Dcp/Model/Executable.cs @@ -6,7 +6,7 @@ namespace Aspire.Hosting.Dcp.Model; using System.Text.Json.Serialization; using k8s.Models; -public class ExecutableSpec +internal sealed class ExecutableSpec { // Path to Executable binary [JsonPropertyName("executablePath")] @@ -33,16 +33,16 @@ public class ExecutableSpec public string? ExecutionType { get; set; } } -public static class ExecutionType +internal static class ExecutionType { // Executable will be run directly by the controller, as a child process - public static readonly string Process = "Process"; + public const string Process = "Process"; // Executable will be run via an IDE such as Visual Studio or Visual Studio Code. - public static readonly string IDE = "IDE"; + public const string IDE = "IDE"; } -public class ExecutableStatus : V1Status +internal sealed class ExecutableStatus : V1Status { // The execution ID is the identifier for the actual-state counterpart of the Executable. // For ExecutionType == Process it is the process ID. Process IDs will be eventually reused by OS, @@ -85,32 +85,32 @@ public class ExecutableStatus : V1Status } -public static class ExecutableStates +internal static class ExecutableStates { // Executable was successfully started and was running last time we checked. - public static readonly string Running = "Running"; + public const string Running = "Running"; // Terminated means the Executable was killed by the controller (e.g. as a result of scale-down, or object deletion). - public static readonly string Terminated = "Terminated"; + public const string Terminated = "Terminated"; // Failed to start means the Executable could not be started (e.g. because of invalid path to program file). - public static readonly string FailedToStart = "FailedToStart"; + public const string FailedToStart = "FailedToStart"; // Finished means the Executable ran to completion. - public static readonly string Finished = "Finished"; + public const string Finished = "Finished"; // Unknown means we are not tracking the actual-state counterpart of the Executable (process or IDE run session). // As a result, we do not know whether it already finished, and what is the exit code, if any. // This can happen if a controller launches a process and then terminates. // When a new controller instance comes online, it may see non-zero ExecutionID Status, // but it does not track the corresponding process or IDE session. - public static readonly string Unknown = "Unknown"; + public const string Unknown = "Unknown"; } -public class Executable : CustomResource +internal sealed class Executable : CustomResource { - public static readonly string CSharpProjectPathAnnotation = "csharp-project-path"; - public static readonly string LaunchProfileNameAnnotation = "launch-profile-name"; + public const string CSharpProjectPathAnnotation = "csharp-project-path"; + public const string LaunchProfileNameAnnotation = "launch-profile-name"; [JsonConstructor] public Executable(ExecutableSpec spec) : base(spec) { } diff --git a/src/Aspire.Hosting/Dcp/Model/ExecutableReplicaSet.cs b/src/Aspire.Hosting/Dcp/Model/ExecutableReplicaSet.cs index e9fa6c157f..60960b4f09 100644 --- a/src/Aspire.Hosting/Dcp/Model/ExecutableReplicaSet.cs +++ b/src/Aspire.Hosting/Dcp/Model/ExecutableReplicaSet.cs @@ -6,7 +6,7 @@ namespace Aspire.Hosting.Dcp.Model; using System.Text.Json.Serialization; using k8s.Models; -public class ExecutableTemplate : IAnnotationHolder +internal sealed class ExecutableTemplate : IAnnotationHolder { // Labels to apply to child Executable objects [JsonPropertyName("labels")] @@ -41,7 +41,7 @@ public void AnnotateAsObjectList(string annotationName, TValue value) } } -public class ExecutableReplicaSetSpec +internal sealed class ExecutableReplicaSetSpec { // Number of desired child Executable objects [JsonPropertyName("replicas")] @@ -52,7 +52,7 @@ public class ExecutableReplicaSetSpec public ExecutableTemplate Template { get; set; } = new ExecutableTemplate(); } -public class ExecutableReplicaSetStatus : V1Status +internal sealed class ExecutableReplicaSetStatus : V1Status { // Total number of observed child executables [JsonPropertyName("observedReplicas")] @@ -75,7 +75,7 @@ public class ExecutableReplicaSetStatus : V1Status public DateTimeOffset? LastScaleTime { get; set; } } -public class ExecutableReplicaSet : CustomResource +internal sealed class ExecutableReplicaSet : CustomResource { [JsonConstructor] public ExecutableReplicaSet(ExecutableReplicaSetSpec spec) : base(spec) { } diff --git a/src/Aspire.Hosting/Dcp/Model/GroupVersion.cs b/src/Aspire.Hosting/Dcp/Model/GroupVersion.cs index 4798200b09..36f1bf2a3b 100644 --- a/src/Aspire.Hosting/Dcp/Model/GroupVersion.cs +++ b/src/Aspire.Hosting/Dcp/Model/GroupVersion.cs @@ -3,7 +3,7 @@ namespace Aspire.Hosting.Dcp.Model; -public struct GroupVersion +internal struct GroupVersion { public string Group { get; set; } public string Version { get; set; } @@ -11,7 +11,7 @@ public struct GroupVersion public override string ToString() => $"{Group}/{Version}"; } -public static class Dcp +internal static class Dcp { public static GroupVersion GroupVersion { get; } = new GroupVersion { diff --git a/src/Aspire.Hosting/Dcp/Model/ModelCommon.cs b/src/Aspire.Hosting/Dcp/Model/ModelCommon.cs index 2e1616784c..eb6dca45d3 100644 --- a/src/Aspire.Hosting/Dcp/Model/ModelCommon.cs +++ b/src/Aspire.Hosting/Dcp/Model/ModelCommon.cs @@ -9,17 +9,17 @@ namespace Aspire.Hosting.Dcp.Model; using k8s; using k8s.Models; -public interface IAnnotationHolder +internal interface IAnnotationHolder { void Annotate(string annotationName, string value); void AnnotateAsObjectList(string annotationName, TValue value); } -public abstract class CustomResource : KubernetesObject, IMetadata, IAnnotationHolder +internal abstract class CustomResource : KubernetesObject, IMetadata, IAnnotationHolder { - public static readonly string ServiceProducerAnnotation = "service-producer"; - public static readonly string ServiceConsumerAnnotation = "service-consumer"; - public static readonly string UriSchemeAnnotation = "uri-scheme"; + public const string ServiceProducerAnnotation = "service-producer"; + public const string ServiceConsumerAnnotation = "service-consumer"; + public const string UriSchemeAnnotation = "uri-scheme"; [JsonPropertyName("metadata")] public V1ObjectMeta Metadata { get; set; } = new V1ObjectMeta(); @@ -66,7 +66,7 @@ internal static void AnnotateAsObjectList(IDictionary an } } -public abstract class CustomResource : CustomResource +internal abstract class CustomResource : CustomResource { [JsonPropertyName("spec")] public TSpec Spec { get; set; } @@ -80,7 +80,7 @@ public CustomResource(TSpec spec) } } -public class CustomResourceList : KubernetesObject +internal sealed class CustomResourceList : KubernetesObject where T : CustomResource { [JsonPropertyName("metadata")] @@ -90,7 +90,7 @@ public class CustomResourceList : KubernetesObject public required List Items { get; set; } } -public class EnvVar +internal sealed class EnvVar { // Name of the environment variable [JsonPropertyName("name")] @@ -101,7 +101,7 @@ public class EnvVar public string? Value { get; set; } } -public static class Conventions +internal static class Conventions { // Indicates that process ID of some process is not known public const int UnknownPID = -1; @@ -110,7 +110,7 @@ public static class Conventions public const int UnknownExitCode = -1; } -public class ServiceProducerAnnotation +internal sealed class ServiceProducerAnnotation { // Name of the service produced [JsonPropertyName("serviceName")] @@ -154,9 +154,9 @@ public override int GetHashCode() } } -public sealed record NamespacedName(string Name, string? Namespace); +internal sealed record NamespacedName(string Name, string? Namespace); -public static class Rules +internal static class Rules { public static bool IsValidObjectName(string candidate) { diff --git a/src/Aspire.Hosting/Dcp/Model/Schema.cs b/src/Aspire.Hosting/Dcp/Model/Schema.cs index a9bf52e1d2..d20e70246c 100644 --- a/src/Aspire.Hosting/Dcp/Model/Schema.cs +++ b/src/Aspire.Hosting/Dcp/Model/Schema.cs @@ -3,7 +3,7 @@ namespace Aspire.Hosting.Dcp.Model; -public class Schema +internal sealed class Schema { private readonly Dictionary _byType = new(); diff --git a/src/Aspire.Hosting/Dcp/Model/Service.cs b/src/Aspire.Hosting/Dcp/Model/Service.cs index 5dd9a233ba..e0226d4f0b 100644 --- a/src/Aspire.Hosting/Dcp/Model/Service.cs +++ b/src/Aspire.Hosting/Dcp/Model/Service.cs @@ -6,7 +6,7 @@ namespace Aspire.Hosting.Dcp.Model; -public class ServiceSpec +internal sealed class ServiceSpec { // The desired address for the service to run on [JsonPropertyName("address")] @@ -25,7 +25,7 @@ public class ServiceSpec public string AddressAllocationMode = AddressAllocationModes.Localhost; } -public class ServiceStatus : V1Status +internal sealed class ServiceStatus : V1Status { // The actual address the service is running on [JsonPropertyName("effectiveAddress")] @@ -40,31 +40,31 @@ public class ServiceStatus : V1Status public string? State { get; set; } } -public static class ServiceState +internal static class ServiceState { // The service is not ready to accept connection. EffectiveAddress and EffectivePort do not contain final data. - public static readonly string NotReady = "NotReady"; + public const string NotReady = "NotReady"; // The service is ready to accept connections. - public static readonly string Ready = "Ready"; + public const string Ready = "Ready"; } -public static class AddressAllocationModes +internal static class AddressAllocationModes { // Bind only to 127.0.0.1 - public static readonly string IPv4ZeroOne = "IPv4ZeroOne"; + public const string IPv4ZeroOne = "IPv4ZeroOne"; // Bind to any 127.*.*.* loopback address range - public static readonly string IPv4Loopback = "IPv4Loopback"; + public const string IPv4Loopback = "IPv4Loopback"; // Bind to IPv6 ::1 - public static readonly string IPv6 = "IPv6ZeroOne"; + public const string IPv6 = "IPv6ZeroOne"; // Bind to "localhost", which is all loopback devices on the machine. - public static readonly string Localhost = "Localhost"; + public const string Localhost = "Localhost"; } -public class Service : CustomResource +internal sealed class Service : CustomResource { [JsonConstructor] public Service(ServiceSpec spec) : base(spec) { } diff --git a/src/Aspire.Hosting/DistributedApplication.cs b/src/Aspire.Hosting/DistributedApplication.cs index 3b533c64dd..91688e7620 100644 --- a/src/Aspire.Hosting/DistributedApplication.cs +++ b/src/Aspire.Hosting/DistributedApplication.cs @@ -9,7 +9,7 @@ namespace Aspire.Hosting; -public enum DockerHealthCheckFailures : int +internal enum DockerHealthCheckFailures : int { Unresponsive = 125, // Invocation of Docker CLI test command did not finish within expected time period. Unhealthy = 126, // The Docker CLI test command returned an error exit code. diff --git a/tests/Aspire.Hosting.Tests/DcpVisibilityTests.cs b/tests/Aspire.Hosting.Tests/DcpVisibilityTests.cs new file mode 100644 index 0000000000..191bb7e9c1 --- /dev/null +++ b/tests/Aspire.Hosting.Tests/DcpVisibilityTests.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace Aspire.Hosting.Tests; +public class DcpVisibilityTests +{ + [Fact] + public void EnsureNoTypesFromDcpNamespaceArePublic() + { + var hostingAssembly = typeof(DistributedApplication).Assembly; + var types = hostingAssembly.GetExportedTypes(); + var dcpNamespaceTypes = types.Where(t => t.FullName!.Contains("Dcp", StringComparison.OrdinalIgnoreCase)); + Assert.Empty(dcpNamespaceTypes); + } +} diff --git a/tests/Aspire.Hosting.Tests/DistributedApplicationBuilderTests.cs b/tests/Aspire.Hosting.Tests/DistributedApplicationBuilderTests.cs index 78cee96dc1..10dd2377e2 100644 --- a/tests/Aspire.Hosting.Tests/DistributedApplicationBuilderTests.cs +++ b/tests/Aspire.Hosting.Tests/DistributedApplicationBuilderTests.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Aspire.Hosting.Dcp; using Aspire.Hosting.Lifecycle; using Aspire.Hosting.Publishing; using Microsoft.Extensions.DependencyInjection; @@ -39,7 +38,7 @@ public void BuilderAddsDefaultServices() Assert.Empty(appModel.Resources); var lifecycles = app.Services.GetServices(); - Assert.Single(lifecycles.OfType()); + Assert.Single(lifecycles.Where(h => h.GetType().Name == "DcpDistributedApplicationLifecycleHook")); Assert.Equal(3, lifecycles.Count()); var options = app.Services.GetRequiredService>();