@@ -73,6 +73,12 @@ internal sealed class ApplicationExecutor(ILogger<ApplicationExecutor> logger,
73
73
{
74
74
private const string DebugSessionPortVar = "DEBUG_SESSION_PORT" ;
75
75
76
+ // A random suffix added to every DCP object name ensures that those names (and derived object names, for example container names)
77
+ // are unique machine-wide with a high level of probability.
78
+ // The length of 8 achieves that while keeping the names relatively short and readable.
79
+ // The second purpose of the suffix is to play a role of a unique OpenTelemetry service instance ID.
80
+ private const int RandomNameSuffixLength = 8 ;
81
+
76
82
private readonly ILogger < ApplicationExecutor > _logger = logger ;
77
83
private readonly DistributedApplicationModel _model = model ;
78
84
private readonly Dictionary < string , IResource > _applicationModel = model . Resources . ToDictionary ( r => r . Name ) ;
@@ -448,8 +454,8 @@ private void StartLogStream<T>(T resource) where T : CustomResource
448
454
{
449
455
IAsyncEnumerable < IReadOnlyList < ( string , bool ) > > ? enumerable = resource switch
450
456
{
451
- Container c when c . LogsAvailable => new ResourceLogSource < T > ( _logger , kubernetesService , resource ) ,
452
- Executable e when e . LogsAvailable => new ResourceLogSource < T > ( _logger , kubernetesService , resource ) ,
457
+ Container c when c . LogsAvailable => new ResourceLogSource < T > ( _logger , kubernetesService , _dcpInfo ? . Version , resource ) ,
458
+ Executable e when e . LogsAvailable => new ResourceLogSource < T > ( _logger , kubernetesService , _dcpInfo ? . Version , resource ) ,
453
459
_ => null
454
460
} ;
455
461
@@ -971,14 +977,16 @@ private void PreparePlainExecutables()
971
977
972
978
foreach ( var executable in modelExecutableResources )
973
979
{
974
- var exeName = GetObjectNameForResource ( executable ) ;
980
+ var nameSuffix = GetRandomNameSuffix ( ) ;
981
+ var exeName = GetObjectNameForResource ( executable , nameSuffix ) ;
975
982
var exePath = executable . Command ;
976
983
var exe = Executable . Create ( exeName , exePath ) ;
977
984
978
985
// The working directory is always relative to the app host project directory (if it exists).
979
986
exe . Spec . WorkingDirectory = executable . WorkingDirectory ;
980
987
exe . Spec . ExecutionType = ExecutionType . Process ;
981
- exe . Annotate ( CustomResource . OtelServiceNameAnnotation , exe . Metadata . Name ) ;
988
+ exe . Annotate ( CustomResource . OtelServiceNameAnnotation , executable . Name ) ;
989
+ exe . Annotate ( CustomResource . OtelServiceInstanceIdAnnotation , nameSuffix ) ;
982
990
exe . Annotate ( CustomResource . ResourceNameAnnotation , executable . Name ) ;
983
991
SetInitialResourceState ( executable , exe ) ;
984
992
@@ -1007,6 +1015,7 @@ private void PrepareProjectExecutables()
1007
1015
1008
1016
IAnnotationHolder annotationHolder = ers . Spec . Template ;
1009
1017
annotationHolder . Annotate ( CustomResource . OtelServiceNameAnnotation , ers . Metadata . Name ) ;
1018
+ // The OTEL service instance ID annotation will be generated and applied automatically by DCP.
1010
1019
annotationHolder . Annotate ( CustomResource . ResourceNameAnnotation , project . Name ) ;
1011
1020
1012
1021
SetInitialResourceState ( project , annotationHolder ) ;
@@ -1018,29 +1027,10 @@ private void PrepareProjectExecutables()
1018
1027
{
1019
1028
exeSpec . ExecutionType = ExecutionType . IDE ;
1020
1029
1021
- if ( _dcpInfo ? . Version ? . CompareTo ( DcpVersion . MinimumVersionIdeProtocolV1 ) >= 0 )
1030
+ projectLaunchConfiguration . DisableLaunchProfile = project . TryGetLastAnnotation < ExcludeLaunchProfileAnnotation > ( out _ ) ;
1031
+ if ( ! projectLaunchConfiguration . DisableLaunchProfile && project . TryGetLastAnnotation < LaunchProfileAnnotation > ( out var lpa ) )
1022
1032
{
1023
- projectLaunchConfiguration . DisableLaunchProfile = project . TryGetLastAnnotation < ExcludeLaunchProfileAnnotation > ( out _ ) ;
1024
- if ( ! projectLaunchConfiguration . DisableLaunchProfile && project . TryGetLastAnnotation < LaunchProfileAnnotation > ( out var lpa ) )
1025
- {
1026
- projectLaunchConfiguration . LaunchProfile = lpa . LaunchProfileName ;
1027
- }
1028
- }
1029
- else
1030
- {
1031
- #pragma warning disable CS0612 // These annotations are obsolete; remove after Aspire GA
1032
- annotationHolder . Annotate ( Executable . CSharpProjectPathAnnotation , projectMetadata . ProjectPath ) ;
1033
-
1034
- // ExcludeLaunchProfileAnnotation takes precedence over LaunchProfileAnnotation.
1035
- if ( project . TryGetLastAnnotation < ExcludeLaunchProfileAnnotation > ( out _ ) )
1036
- {
1037
- annotationHolder . Annotate ( Executable . CSharpDisableLaunchProfileAnnotation , "true" ) ;
1038
- }
1039
- else if ( project . TryGetLastAnnotation < LaunchProfileAnnotation > ( out var lpa ) )
1040
- {
1041
- annotationHolder . Annotate ( Executable . CSharpLaunchProfileAnnotation , lpa . LaunchProfileName ) ;
1042
- }
1043
- #pragma warning restore CS0612
1033
+ projectLaunchConfiguration . LaunchProfile = lpa . LaunchProfileName ;
1044
1034
}
1045
1035
}
1046
1036
else
@@ -1327,10 +1317,14 @@ private void PrepareContainers()
1327
1317
throw new InvalidOperationException ( ) ;
1328
1318
}
1329
1319
1330
- var ctr = Container . Create ( GetObjectNameForResource ( container ) , containerImageName ) ;
1320
+ var nameSuffix = GetRandomNameSuffix ( ) ;
1321
+ var containerObjectName = GetObjectNameForResource ( container , nameSuffix ) ;
1322
+ var ctr = Container . Create ( containerObjectName , containerImageName ) ;
1331
1323
1324
+ ctr . Spec . ContainerName = containerObjectName ; // Use the same name for container orchestrator (Docker, Podman) resource and DCP object name.
1332
1325
ctr . Annotate ( CustomResource . ResourceNameAnnotation , container . Name ) ;
1333
1326
ctr . Annotate ( CustomResource . OtelServiceNameAnnotation , container . Name ) ;
1327
+ ctr . Annotate ( CustomResource . OtelServiceInstanceIdAnnotation , nameSuffix ) ;
1334
1328
SetInitialResourceState ( container , ctr ) ;
1335
1329
1336
1330
if ( container . TryGetContainerMounts ( out var containerMounts ) )
@@ -1739,9 +1733,9 @@ static string maybeWithSuffix(string s, string localSuffix, string? globalSuffix
1739
1733
=> ( string . IsNullOrWhiteSpace ( localSuffix ) , string . IsNullOrWhiteSpace ( globalSuffix ) ) switch
1740
1734
{
1741
1735
( true , true ) => s ,
1742
- ( false , true ) => $ "{ s } _ { localSuffix } ",
1743
- ( true , false ) => $ "{ s } _ { globalSuffix } ",
1744
- ( false , false ) => $ "{ s } _ { localSuffix } _ { globalSuffix } "
1736
+ ( false , true ) => $ "{ s } - { localSuffix } ",
1737
+ ( true , false ) => $ "{ s } - { globalSuffix } ",
1738
+ ( false , false ) => $ "{ s } - { localSuffix } - { globalSuffix } "
1745
1739
} ;
1746
1740
return maybeWithSuffix ( resource . Name , suffix , _options . Value . ResourceNameSuffix ) ;
1747
1741
}
@@ -1753,7 +1747,7 @@ private static string GenerateUniqueServiceName(HashSet<string> serviceNames, st
1753
1747
1754
1748
while ( ! serviceNames . Add ( uniqueName ) )
1755
1749
{
1756
- uniqueName = $ "{ candidateName } _ { suffix } ";
1750
+ uniqueName = $ "{ candidateName } - { suffix } ";
1757
1751
suffix ++ ;
1758
1752
if ( suffix == 100 )
1759
1753
{
@@ -1765,6 +1759,13 @@ private static string GenerateUniqueServiceName(HashSet<string> serviceNames, st
1765
1759
return uniqueName ;
1766
1760
}
1767
1761
1762
+ private static string GetRandomNameSuffix ( )
1763
+ {
1764
+ // RandomNameSuffixLength of lowercase characters
1765
+ var suffix = PasswordGenerator . Generate ( RandomNameSuffixLength , true , false , false , false , RandomNameSuffixLength , 0 , 0 , 0 ) ;
1766
+ return suffix ;
1767
+ }
1768
+
1768
1769
public async Task DeleteResourcesAsync ( CancellationToken cancellationToken = default )
1769
1770
{
1770
1771
try
0 commit comments