From bae8d35418cfff6b57fa7584935003ddad2e51b1 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Mon, 14 Oct 2024 10:31:55 +0800 Subject: [PATCH 1/3] Add argument null validation to Aspire.Hosting --- .../ContainerResourceBuilderExtensions.cs | 62 +++++++++++ .../ContainerResourceExtensions.cs | 4 + .../ExecutableResourceBuilderExtensions.cs | 12 +++ .../ExecutableResourceExtensions.cs | 2 + .../OtlpConfigurationExtensions.cs | 6 ++ .../ParameterResourceBuilderExtensions.cs | 32 ++++++ .../ProjectResourceBuilderExtensions.cs | 30 ++++++ .../ResourceBuilderExtensions.cs | 102 ++++++++++++++++++ .../ContainerResourceTests.cs | 2 +- 9 files changed, 251 insertions(+), 1 deletion(-) diff --git a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs index 041dac2ba5e..dae57249579 100644 --- a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs @@ -20,6 +20,10 @@ public static class ContainerResourceBuilderExtensions /// The for chaining. public static IResourceBuilder AddContainer(this IDistributedApplicationBuilder builder, [ResourceName] string name, string image) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(image); + return builder.AddContainer(name, image, "latest"); } @@ -33,6 +37,11 @@ public static IResourceBuilder AddContainer(this IDistributed /// The for chaining. public static IResourceBuilder AddContainer(this IDistributedApplicationBuilder builder, [ResourceName] string name, string image, string tag) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(tag); + var container = new ContainerResource(name); return builder.AddResource(container) .WithImage(image, tag); @@ -49,6 +58,10 @@ public static IResourceBuilder AddContainer(this IDistributed /// The . public static IResourceBuilder WithVolume(this IResourceBuilder builder, string name, string target, bool isReadOnly = false) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(target); + var annotation = new ContainerMountAnnotation(name, target, ContainerMountType.Volume, isReadOnly); return builder.WithAnnotation(annotation); } @@ -62,6 +75,9 @@ public static IResourceBuilder WithVolume(this IResourceBuilder builder /// The . public static IResourceBuilder WithVolume(this IResourceBuilder builder, string target) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(target); + var annotation = new ContainerMountAnnotation(null, target, ContainerMountType.Volume, false); return builder.WithAnnotation(annotation); } @@ -77,6 +93,10 @@ public static IResourceBuilder WithVolume(this IResourceBuilder builder /// The . public static IResourceBuilder WithBindMount(this IResourceBuilder builder, string source, string target, bool isReadOnly = false) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(source); + ArgumentNullException.ThrowIfNull(target); + var annotation = new ContainerMountAnnotation(Path.GetFullPath(source, builder.ApplicationBuilder.AppHostDirectory), target, ContainerMountType.BindMount, isReadOnly); return builder.WithAnnotation(annotation); } @@ -90,6 +110,9 @@ public static IResourceBuilder WithBindMount(this IResourceBuilder buil /// The . public static IResourceBuilder WithEntrypoint(this IResourceBuilder builder, string entrypoint) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(entrypoint); + builder.Resource.Entrypoint = entrypoint; return builder; } @@ -103,6 +126,9 @@ public static IResourceBuilder WithEntrypoint(this IResourceBuilder bui /// public static IResourceBuilder WithImageTag(this IResourceBuilder builder, string tag) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(tag); + if (builder.Resource.Annotations.OfType().LastOrDefault() is { } existingImageAnnotation) { existingImageAnnotation.Tag = tag; @@ -121,6 +147,9 @@ public static IResourceBuilder WithImageTag(this IResourceBuilder build /// public static IResourceBuilder WithImageRegistry(this IResourceBuilder builder, string registry) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(registry); + if (builder.Resource.Annotations.OfType().LastOrDefault() is { } existingImageAnnotation) { existingImageAnnotation.Registry = registry; @@ -140,6 +169,10 @@ public static IResourceBuilder WithImageRegistry(this IResourceBuilder /// public static IResourceBuilder WithImage(this IResourceBuilder builder, string image, string tag = "latest") where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(tag); + if (builder.Resource.Annotations.OfType().LastOrDefault() is { } existingImageAnnotation) { existingImageAnnotation.Image = image; @@ -162,6 +195,9 @@ public static IResourceBuilder WithImage(this IResourceBuilder builder, /// public static IResourceBuilder WithImageSHA256(this IResourceBuilder builder, string sha256) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(sha256); + if (builder.Resource.Annotations.OfType().LastOrDefault() is { } existingImageAnnotation) { existingImageAnnotation.SHA256 = sha256; @@ -183,6 +219,8 @@ public static IResourceBuilder WithImageSHA256(this IResourceBuilder bu /// The . public static IResourceBuilder WithContainerRuntimeArgs(this IResourceBuilder builder, params string[] args) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + return builder.WithContainerRuntimeArgs(context => context.Args.AddRange(args)); } @@ -198,6 +236,9 @@ public static IResourceBuilder WithContainerRuntimeArgs(this IResourceBuil /// The . public static IResourceBuilder WithContainerRuntimeArgs(this IResourceBuilder builder, Action callback) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(callback); + return builder.WithContainerRuntimeArgs(context => { callback(context); @@ -217,6 +258,9 @@ public static IResourceBuilder WithContainerRuntimeArgs(this IResourceBuil /// The . public static IResourceBuilder WithContainerRuntimeArgs(this IResourceBuilder builder, Func callback) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(callback); + var annotation = new ContainerRuntimeArgsCallbackAnnotation(callback); return builder.WithAnnotation(annotation); } @@ -238,6 +282,8 @@ public static IResourceBuilder WithContainerRuntimeArgs(this IResourceBuil /// public static IResourceBuilder WithLifetime(this IResourceBuilder builder, ContainerLifetime lifetime) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + return builder.WithAnnotation(new ContainerLifetimeAnnotation { Lifetime = lifetime }, ResourceAnnotationMutationBehavior.Replace); } @@ -253,6 +299,8 @@ private static IResourceBuilder ThrowResourceIsNotContainer(IResourceBuild /// A reference to the . public static IResourceBuilder PublishAsContainer(this IResourceBuilder builder) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + return builder.WithManifestPublishingCallback(context => context.WriteContainerAsync(builder.Resource)); } @@ -293,6 +341,7 @@ public static IResourceBuilder PublishAsContainer(this IResourceBuilder /// public static IResourceBuilder WithDockerfile(this IResourceBuilder builder, string contextPath, string? dockerfilePath = null, string? stage = null) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); ArgumentException.ThrowIfNullOrEmpty(contextPath); var fullyQualifiedContextPath = Path.GetFullPath(contextPath, builder.ApplicationBuilder.AppHostDirectory); @@ -350,6 +399,10 @@ public static IResourceBuilder WithDockerfile(this IResourceBuilder bui /// public static IResourceBuilder AddDockerfile(this IDistributedApplicationBuilder builder, [ResourceName] string name, string contextPath, string? dockerfilePath = null, string? stage = null) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(contextPath); + return builder.AddContainer(name, "placeholder") // Image name will be replaced by WithDockerfile. .WithDockerfile(contextPath, dockerfilePath, stage); } @@ -369,6 +422,9 @@ public static IResourceBuilder AddDockerfile(this IDistribute /// The resource bulder for the container resource. public static IResourceBuilder WithContainerName(this IResourceBuilder builder, string name) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + return builder.WithAnnotation(new ContainerNameAnnotation { Name = name }, ResourceAnnotationMutationBehavior.Replace); } @@ -402,6 +458,7 @@ public static IResourceBuilder WithContainerName(this IResourceBuilder /// public static IResourceBuilder WithBuildArg(this IResourceBuilder builder, string name, object value) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); ArgumentException.ThrowIfNullOrEmpty(name); ArgumentNullException.ThrowIfNull(value); @@ -448,6 +505,10 @@ public static IResourceBuilder WithBuildArg(this IResourceBuilder build /// public static IResourceBuilder WithBuildArg(this IResourceBuilder builder, string name, IResourceBuilder value) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(value); + if (value.Resource.Secret) { throw new InvalidOperationException("Cannot add secret parameter as a build argument. Use WithSecretBuildArg instead."); @@ -487,6 +548,7 @@ public static IResourceBuilder WithBuildArg(this IResourceBuilder build /// public static IResourceBuilder WithBuildSecret(this IResourceBuilder builder, string name, IResourceBuilder value) where T : ContainerResource { + ArgumentNullException.ThrowIfNull(builder); ArgumentException.ThrowIfNullOrEmpty(name); ArgumentNullException.ThrowIfNull(value); diff --git a/src/Aspire.Hosting/ContainerResourceExtensions.cs b/src/Aspire.Hosting/ContainerResourceExtensions.cs index 5c520aea8be..ea48e0b5575 100644 --- a/src/Aspire.Hosting/ContainerResourceExtensions.cs +++ b/src/Aspire.Hosting/ContainerResourceExtensions.cs @@ -17,6 +17,8 @@ public static class ContainerResourceExtensions /// A collection of container resources in the specified distributed application model. public static IEnumerable GetContainerResources(this DistributedApplicationModel model) { + ArgumentNullException.ThrowIfNull(model); + foreach (var resource in model.Resources) { if (resource.Annotations.OfType().Any()) @@ -33,6 +35,8 @@ public static IEnumerable GetContainerResources(this DistributedAppli /// true if the specified resource is a container resource; otherwise, false. public static bool IsContainer(this IResource resource) { + ArgumentNullException.ThrowIfNull(resource); + return resource.Annotations.OfType().Any(); } } diff --git a/src/Aspire.Hosting/ExecutableResourceBuilderExtensions.cs b/src/Aspire.Hosting/ExecutableResourceBuilderExtensions.cs index dc402967b10..8c16df8404f 100644 --- a/src/Aspire.Hosting/ExecutableResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ExecutableResourceBuilderExtensions.cs @@ -23,6 +23,11 @@ public static class ExecutableResourceBuilderExtensions /// The . public static IResourceBuilder AddExecutable(this IDistributedApplicationBuilder builder, [ResourceName] string name, string command, string workingDirectory, params string[]? args) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(command); + ArgumentNullException.ThrowIfNull(workingDirectory); + return AddExecutable(builder, name, command, workingDirectory, (object[]?)args); } @@ -37,6 +42,11 @@ public static IResourceBuilder AddExecutable(this IDistribut /// The . public static IResourceBuilder AddExecutable(this IDistributedApplicationBuilder builder, [ResourceName] string name, string command, string workingDirectory, params object[]? args) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(command); + ArgumentNullException.ThrowIfNull(workingDirectory); + workingDirectory = PathNormalizer.NormalizePathForCurrentPlatform(Path.Combine(builder.AppHostDirectory, workingDirectory)); var executable = new ExecutableResource(name, command, workingDirectory); @@ -61,6 +71,8 @@ public static IResourceBuilder AddExecutable(this IDistribut /// A reference to the . public static IResourceBuilder PublishAsDockerFile(this IResourceBuilder builder, IEnumerable? buildArgs = null) where T : ExecutableResource { + ArgumentNullException.ThrowIfNull(builder); + return builder.WithManifestPublishingCallback(context => WriteExecutableAsDockerfileResourceAsync(context, builder.Resource, buildArgs)); } diff --git a/src/Aspire.Hosting/ExecutableResourceExtensions.cs b/src/Aspire.Hosting/ExecutableResourceExtensions.cs index 5b7604a1ad6..69fccd1f39a 100644 --- a/src/Aspire.Hosting/ExecutableResourceExtensions.cs +++ b/src/Aspire.Hosting/ExecutableResourceExtensions.cs @@ -17,6 +17,8 @@ public static class ExecutableResourceExtensions /// An enumerable collection of executable resources. public static IEnumerable GetExecutableResources(this DistributedApplicationModel model) { + ArgumentNullException.ThrowIfNull(model); + return model.Resources.OfType(); } } diff --git a/src/Aspire.Hosting/OtlpConfigurationExtensions.cs b/src/Aspire.Hosting/OtlpConfigurationExtensions.cs index e08c74f94a4..ffed6b77be1 100644 --- a/src/Aspire.Hosting/OtlpConfigurationExtensions.cs +++ b/src/Aspire.Hosting/OtlpConfigurationExtensions.cs @@ -27,6 +27,10 @@ public static class OtlpConfigurationExtensions /// The host environment to check if the application is running in development mode. public static void AddOtlpEnvironment(IResource resource, IConfiguration configuration, IHostEnvironment environment) { + ArgumentNullException.ThrowIfNull(resource); + ArgumentNullException.ThrowIfNull(configuration); + ArgumentNullException.ThrowIfNull(environment); + // Configure OpenTelemetry in projects using environment variables. // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md @@ -112,6 +116,8 @@ static void SetOtelEndpointAndProtocol(Dictionary environmentVar /// The . public static IResourceBuilder WithOtlpExporter(this IResourceBuilder builder) where T : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + AddOtlpEnvironment(builder.Resource, builder.ApplicationBuilder.Configuration, builder.ApplicationBuilder.Environment); return builder; } diff --git a/src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs b/src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs index ebd8006409f..04473465aae 100644 --- a/src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs @@ -24,6 +24,9 @@ public static class ParameterResourceBuilderExtensions /// public static IResourceBuilder AddParameter(this IDistributedApplicationBuilder builder, [ResourceName] string name, bool secret = false) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + return builder.AddParameter(name, parameterDefault => GetParameterValue(builder.Configuration, name, parameterDefault), secret: secret); } @@ -41,6 +44,10 @@ public static IResourceBuilder AddParameter(this IDistributed Justification = "third parameters are mutually exclusive.")] public static IResourceBuilder AddParameter(this IDistributedApplicationBuilder builder, [ResourceName] string name, string value, bool publishValueAsDefault = false, bool secret = false) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(value); + return builder.AddParameter(name, () => value, publishValueAsDefault, secret); } @@ -58,6 +65,10 @@ public static IResourceBuilder AddParameter(this IDistributed Justification = "third parameters are mutually exclusive.")] public static IResourceBuilder AddParameter(this IDistributedApplicationBuilder builder, string name, Func valueGetter, bool publishValueAsDefault = false, bool secret = false) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(valueGetter); + // We don't allow a parameter to be both secret and published, as that would write the secret to the manifest. if (publishValueAsDefault && secret) { @@ -83,6 +94,10 @@ public static IResourceBuilder AddParameter(this IDistributed /// Resource builder for the parameter. public static IResourceBuilder AddParameterFromConfiguration(this IDistributedApplicationBuilder builder, string name, string configurationKey, bool secret = false) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configurationKey); + return builder.AddParameter( name, parameterDefault => GetParameterValue(builder.Configuration, name, parameterDefault, configurationKey), @@ -106,6 +121,10 @@ public static IResourceBuilder AddParameterFromConfiguration( Justification = "third parameters are mutually exclusive.")] public static IResourceBuilder AddParameter(this IDistributedApplicationBuilder builder, [ResourceName] string name, ParameterDefault value, bool secret = false, bool persist = false) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(value); + // If it needs persistence, wrap it in a UserSecretsParameterDefault if (persist && builder.ExecutionContext.IsRunMode && builder.AppHostAssembly is not null) { @@ -197,6 +216,9 @@ public Task BeforeStartAsync(DistributedApplicationModel appModel, CancellationT /// public static IResourceBuilder AddConnectionString(this IDistributedApplicationBuilder builder, [ResourceName] string name, string? environmentVariableName = null) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + var parameterBuilder = builder.AddParameter(name, _ => { return builder.Configuration.GetConnectionString(name) ?? throw new DistributedApplicationException($"Connection string parameter resource could not be used because connection string '{name}' is missing."); @@ -217,6 +239,8 @@ public static IResourceBuilder AddConnectionStrin public static IResourceBuilder PublishAsConnectionString(this IResourceBuilder builder) where T : ContainerResource, IResourceWithConnectionString { + ArgumentNullException.ThrowIfNull(builder); + ConfigureConnectionStringManifestPublisher(builder); return builder; } @@ -227,6 +251,8 @@ public static IResourceBuilder PublishAsConnectionString(this IResourceBui /// The . public static void ConfigureConnectionStringManifestPublisher(IResourceBuilder builder) { + ArgumentNullException.ThrowIfNull(builder); + // Create a parameter resource that we use to write to the manifest var parameter = new ParameterResource(builder.Resource.Name, _ => "", secret: true); parameter.IsConnectionString = true; @@ -257,6 +283,9 @@ public static ParameterResource CreateDefaultPasswordParameter( bool lower = true, bool upper = true, bool numeric = true, bool special = true, int minLower = 0, int minUpper = 0, int minNumeric = 0, int minSpecial = 0) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + var generatedPassword = new GenerateParameterDefault { MinLength = 22, // enough to give 128 bits of entropy when using the default 67 possible characters. See remarks in GenerateParameterDefault @@ -287,6 +316,9 @@ public static ParameterResource CreateDefaultPasswordParameter( public static ParameterResource CreateGeneratedParameter( IDistributedApplicationBuilder builder, string name, bool secret, GenerateParameterDefault parameterDefault) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + var parameterResource = new ParameterResource(name, defaultValue => GetParameterValue(builder.Configuration, name, defaultValue), secret) { Default = parameterDefault diff --git a/src/Aspire.Hosting/ProjectResourceBuilderExtensions.cs b/src/Aspire.Hosting/ProjectResourceBuilderExtensions.cs index be0b4aeb4ec..e63b78081a1 100644 --- a/src/Aspire.Hosting/ProjectResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ProjectResourceBuilderExtensions.cs @@ -60,6 +60,9 @@ public static class ProjectResourceBuilderExtensions /// public static IResourceBuilder AddProject(this IDistributedApplicationBuilder builder, [ResourceName] string name) where TProject : IProjectMetadata, new() { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + return builder.AddProject(name, _ => { }); } @@ -90,6 +93,10 @@ public static class ProjectResourceBuilderExtensions /// public static IResourceBuilder AddProject(this IDistributedApplicationBuilder builder, [ResourceName] string name, string projectPath) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(projectPath); + return builder.AddProject(name, projectPath, _ => { }); } @@ -133,6 +140,9 @@ public static IResourceBuilder AddProject(this IDistributedAppl /// public static IResourceBuilder AddProject(this IDistributedApplicationBuilder builder, [ResourceName] string name, string? launchProfileName) where TProject : IProjectMetadata, new() { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + return builder.AddProject(name, options => { options.ExcludeLaunchProfile = launchProfileName is null; @@ -168,6 +178,10 @@ public static IResourceBuilder AddProject(this IDistributedAppl /// public static IResourceBuilder AddProject(this IDistributedApplicationBuilder builder, [ResourceName] string name, string projectPath, string? launchProfileName) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(projectPath); + return builder.AddProject(name, projectPath, options => { options.ExcludeLaunchProfile = launchProfileName is null; @@ -213,6 +227,10 @@ public static IResourceBuilder AddProject(this IDistributedAppl /// public static IResourceBuilder AddProject(this IDistributedApplicationBuilder builder, [ResourceName] string name, Action configure) where TProject : IProjectMetadata, new() { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(configure); + var options = new ProjectResourceOptions(); configure(options); @@ -249,6 +267,11 @@ public static IResourceBuilder AddProject(this IDistributedAppl /// public static IResourceBuilder AddProject(this IDistributedApplicationBuilder builder, [ResourceName] string name, string projectPath, Action configure) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(projectPath); + ArgumentNullException.ThrowIfNull(configure); + var options = new ProjectResourceOptions(); configure(options); @@ -539,6 +562,8 @@ EndpointAnnotation GetOrCreateEndpointForScheme(string scheme) /// public static IResourceBuilder WithReplicas(this IResourceBuilder builder, int replicas) { + ArgumentNullException.ThrowIfNull(builder); + builder.WithAnnotation(new ReplicaAnnotation(replicas)); return builder; } @@ -571,6 +596,8 @@ public static IResourceBuilder WithReplicas(this IResourceBuild /// public static IResourceBuilder DisableForwardedHeaders(this IResourceBuilder builder) { + ArgumentNullException.ThrowIfNull(builder); + builder.WithAnnotation(ResourceAnnotationMutationBehavior.Replace); return builder; } @@ -585,6 +612,9 @@ public static IResourceBuilder DisableForwardedHeaders(this IRe public static IResourceBuilder WithEndpointsInEnvironment( this IResourceBuilder builder, Func filter) { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(filter); + builder.Resource.Annotations.Add(new EndpointEnvironmentInjectionFilterAnnotation(filter)); return builder; diff --git a/src/Aspire.Hosting/ResourceBuilderExtensions.cs b/src/Aspire.Hosting/ResourceBuilderExtensions.cs index c2457cbae19..5fec612b95d 100644 --- a/src/Aspire.Hosting/ResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ResourceBuilderExtensions.cs @@ -28,6 +28,9 @@ public static class ResourceBuilderExtensions /// A resource configured with the specified environment variable. public static IResourceBuilder WithEnvironment(this IResourceBuilder builder, string name, string? value) where T : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + return builder.WithAnnotation(new EnvironmentAnnotation(name, value ?? string.Empty)); } @@ -42,6 +45,9 @@ public static IResourceBuilder WithEnvironment(this IResourceBuilder bu public static IResourceBuilder WithEnvironment(this IResourceBuilder builder, string name, in ReferenceExpression.ExpressionInterpolatedStringHandler value) where T : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + var expression = value.GetExpression(); return builder.WithEnvironment(context => @@ -61,6 +67,10 @@ public static IResourceBuilder WithEnvironment(this IResourceBuilder bu public static IResourceBuilder WithEnvironment(this IResourceBuilder builder, string name, ReferenceExpression value) where T : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(value); + return builder.WithEnvironment(context => { context.EnvironmentVariables[name] = value; @@ -77,6 +87,10 @@ public static IResourceBuilder WithEnvironment(this IResourceBuilder bu /// A resource configured with the specified environment variable. public static IResourceBuilder WithEnvironment(this IResourceBuilder builder, string name, Func callback) where T : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(callback); + return builder.WithAnnotation(new EnvironmentCallbackAnnotation(name, callback)); } @@ -89,6 +103,9 @@ public static IResourceBuilder WithEnvironment(this IResourceBuilder bu /// A resource configured with the environment variable callback. public static IResourceBuilder WithEnvironment(this IResourceBuilder builder, Action callback) where T : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(callback); + return builder.WithAnnotation(new EnvironmentCallbackAnnotation(callback)); } @@ -101,6 +118,9 @@ public static IResourceBuilder WithEnvironment(this IResourceBuilder bu /// A resource configured with the environment variable callback. public static IResourceBuilder WithEnvironment(this IResourceBuilder builder, Func callback) where T : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(callback); + return builder.WithAnnotation(new EnvironmentCallbackAnnotation(callback)); } @@ -114,6 +134,10 @@ public static IResourceBuilder WithEnvironment(this IResourceBuilder bu /// A resource configured with the environment variable callback. public static IResourceBuilder WithEnvironment(this IResourceBuilder builder, string name, EndpointReference endpointReference) where T : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(endpointReference); + return builder.WithEnvironment(context => { context.EnvironmentVariables[name] = endpointReference; @@ -130,6 +154,10 @@ public static IResourceBuilder WithEnvironment(this IResourceBuilder bu /// A resource configured with the environment variable callback. public static IResourceBuilder WithEnvironment(this IResourceBuilder builder, string name, IResourceBuilder parameter) where T : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(parameter); + return builder.WithEnvironment(context => { context.EnvironmentVariables[name] = parameter.Resource; @@ -150,6 +178,10 @@ public static IResourceBuilder WithEnvironment( IResourceBuilder resource) where T : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(envVarName); + ArgumentNullException.ThrowIfNull(resource); + return builder.WithEnvironment(context => { context.EnvironmentVariables[envVarName] = new ConnectionStringReference(resource.Resource, optional: false); @@ -165,6 +197,9 @@ public static IResourceBuilder WithEnvironment( /// The . public static IResourceBuilder WithArgs(this IResourceBuilder builder, params string[] args) where T : IResourceWithArgs { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(args); + return builder.WithArgs(context => context.Args.AddRange(args)); } @@ -177,6 +212,9 @@ public static IResourceBuilder WithArgs(this IResourceBuilder builder, /// The . public static IResourceBuilder WithArgs(this IResourceBuilder builder, params object[] args) where T : IResourceWithArgs { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(args); + return builder.WithArgs(context => context.Args.AddRange(args)); } @@ -189,6 +227,9 @@ public static IResourceBuilder WithArgs(this IResourceBuilder builder, /// The . public static IResourceBuilder WithArgs(this IResourceBuilder builder, Action callback) where T : IResourceWithArgs { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(callback); + return builder.WithArgs(context => { callback(context); @@ -205,6 +246,9 @@ public static IResourceBuilder WithArgs(this IResourceBuilder builder, /// The . public static IResourceBuilder WithArgs(this IResourceBuilder builder, Func callback) where T : IResourceWithArgs { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(callback); + return builder.WithAnnotation(new CommandLineArgsCallbackAnnotation(callback)); } @@ -217,6 +261,9 @@ public static IResourceBuilder WithArgs(this IResourceBuilder builder, /// A reference to the . public static IResourceBuilder WithManifestPublishingCallback(this IResourceBuilder builder, Action callback) where T : IResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(callback); + // You can only ever have one manifest publishing callback, so it must be a replace operation. return builder.WithAnnotation(new ManifestPublishingCallbackAnnotation(callback), ResourceAnnotationMutationBehavior.Replace); } @@ -230,6 +277,9 @@ public static IResourceBuilder WithManifestPublishingCallback(this IResour /// A reference to the . public static IResourceBuilder WithManifestPublishingCallback(this IResourceBuilder builder, Func callback) where T : IResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(callback); + // You can only ever have one manifest publishing callback, so it must be a replace operation. return builder.WithAnnotation(new ManifestPublishingCallbackAnnotation(callback), ResourceAnnotationMutationBehavior.Replace); } @@ -243,6 +293,9 @@ public static IResourceBuilder WithManifestPublishingCallback(this IResour /// A reference to the . public static IResourceBuilder WithConnectionStringRedirection(this IResourceBuilder builder, IResourceWithConnectionString resource) where T : IResourceWithConnectionString { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(resource); + // You can only ever have one manifest publishing callback, so it must be a replace operation. return builder.WithAnnotation(new ConnectionStringRedirectAnnotation(resource), ResourceAnnotationMutationBehavior.Replace); } @@ -290,6 +343,9 @@ private static Action CreateEndpointReferenceEnviron public static IResourceBuilder WithReference(this IResourceBuilder builder, IResourceBuilder source, string? connectionName = null, bool optional = false) where TDestination : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(source); + var resource = source.Resource; connectionName ??= resource.Name; @@ -312,6 +368,9 @@ public static IResourceBuilder WithReference(this IR public static IResourceBuilder WithReference(this IResourceBuilder builder, IResourceBuilder source) where TDestination : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(source); + ApplyEndpoints(builder, source.Resource); return builder; } @@ -328,6 +387,10 @@ public static IResourceBuilder WithReference(this IR public static IResourceBuilder WithReference(this IResourceBuilder builder, string name, Uri uri) where TDestination : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(name); + ArgumentNullException.ThrowIfNull(uri); + if (!uri.IsAbsoluteUri) { throw new InvalidOperationException("The uri for service reference must be absolute."); @@ -352,6 +415,9 @@ public static IResourceBuilder WithReference(this IR public static IResourceBuilder WithReference(this IResourceBuilder builder, EndpointReference endpointReference) where TDestination : IResourceWithEnvironment { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(endpointReference); + ApplyEndpoints(builder, endpointReference.Resource, endpointReference.EndpointName); return builder; } @@ -399,6 +465,10 @@ private static void ApplyEndpoints(this IResourceBuilder builder, IResourc [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "")] public static IResourceBuilder WithEndpoint(this IResourceBuilder builder, [EndpointName] string endpointName, Action callback, bool createIfNotExists = true) where T : IResourceWithEndpoints { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(endpointName); + ArgumentNullException.ThrowIfNull(callback); + var endpoint = builder.Resource.Annotations .OfType() .Where(ea => StringComparers.EndpointAnnotationName.Equals(ea.Name, endpointName)) @@ -441,6 +511,8 @@ public static IResourceBuilder WithEndpoint(this IResourceBuilder build [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "")] public static IResourceBuilder WithEndpoint(this IResourceBuilder builder, int? port = null, int? targetPort = null, string? scheme = null, [EndpointName] string? name = null, string? env = null, bool isProxied = true, bool? isExternal = null) where T : IResourceWithEndpoints { + ArgumentNullException.ThrowIfNull(builder); + var annotation = new EndpointAnnotation( protocol: ProtocolType.Tcp, uriScheme: scheme, @@ -486,6 +558,8 @@ public static IResourceBuilder WithEndpoint(this IResourceBuilder build /// Throws an exception if an endpoint with the same name already exists on the specified resource. public static IResourceBuilder WithHttpEndpoint(this IResourceBuilder builder, int? port = null, int? targetPort = null, [EndpointName] string? name = null, string? env = null, bool isProxied = true) where T : IResourceWithEndpoints { + ArgumentNullException.ThrowIfNull(builder); + return builder.WithEndpoint(targetPort: targetPort, port: port, scheme: "http", name: name, env: env, isProxied: isProxied); } @@ -504,6 +578,8 @@ public static IResourceBuilder WithHttpEndpoint(this IResourceBuilder b /// Throws an exception if an endpoint with the same name already exists on the specified resource. public static IResourceBuilder WithHttpsEndpoint(this IResourceBuilder builder, int? port = null, int? targetPort = null, [EndpointName] string? name = null, string? env = null, bool isProxied = true) where T : IResourceWithEndpoints { + ArgumentNullException.ThrowIfNull(builder); + return builder.WithEndpoint(targetPort: targetPort, port: port, scheme: "https", name: name, env: env, isProxied: isProxied); } @@ -515,6 +591,8 @@ public static IResourceBuilder WithHttpsEndpoint(this IResourceBuilder /// public static IResourceBuilder WithExternalHttpEndpoints(this IResourceBuilder builder) where T : IResourceWithEndpoints { + ArgumentNullException.ThrowIfNull(builder); + if (!builder.Resource.TryGetAnnotationsOfType(out var endpoints)) { return builder; @@ -541,6 +619,8 @@ public static IResourceBuilder WithExternalHttpEndpoints(this IResourceBui /// An that can be used to resolve the address of the endpoint after resource allocation has occurred. public static EndpointReference GetEndpoint(this IResourceBuilder builder, [EndpointName] string name) where T : IResourceWithEndpoints { + ArgumentNullException.ThrowIfNull(builder); + return builder.Resource.GetEndpoint(name); } @@ -552,6 +632,8 @@ public static EndpointReference GetEndpoint(this IResourceBuilder builder, /// The . public static IResourceBuilder AsHttp2Service(this IResourceBuilder builder) where T : IResourceWithEndpoints { + ArgumentNullException.ThrowIfNull(builder); + return builder.WithAnnotation(new Http2ServiceAnnotation()); } @@ -563,6 +645,8 @@ public static IResourceBuilder AsHttp2Service(this IResourceBuilder bui /// The . public static IResourceBuilder ExcludeFromManifest(this IResourceBuilder builder) where T : IResource { + ArgumentNullException.ThrowIfNull(builder); + return builder.WithAnnotation(ManifestPublishingCallbackAnnotation.Ignore); } @@ -595,6 +679,9 @@ public static IResourceBuilder ExcludeFromManifest(this IResourceBuilder public static IResourceBuilder WaitFor(this IResourceBuilder builder, IResourceBuilder dependency) where T : IResourceWithWaitSupport { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(dependency); + if (builder.Resource as IResource == dependency.Resource) { throw new DistributedApplicationException($"The '{builder.Resource.Name}' resource cannot wait for itself."); @@ -645,6 +732,9 @@ public static IResourceBuilder WaitFor(this IResourceBuilder builder, I /// public static IResourceBuilder WaitForCompletion(this IResourceBuilder builder, IResourceBuilder dependency, int exitCode = 0) where T : IResourceWithWaitSupport { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(dependency); + if (builder.Resource as IResource == dependency.Resource) { throw new DistributedApplicationException($"The '{builder.Resource.Name}' resource cannot wait for itself."); @@ -696,6 +786,9 @@ public static IResourceBuilder WaitForCompletion(this IResourceBuilder /// public static IResourceBuilder WithHealthCheck(this IResourceBuilder builder, string key) where T : IResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(key); + if (builder.Resource.TryGetAnnotationsOfType(out var annotations) && annotations.Any(a => a.Key == key)) { throw new DistributedApplicationException($"Resource '{builder.Resource.Name}' already has a health check with key '{key}'."); @@ -737,6 +830,8 @@ public static IResourceBuilder WithHealthCheck(this IResourceBuilder bu /// public static IResourceBuilder WithHttpHealthCheck(this IResourceBuilder builder, string? path = null, int? statusCode = null, string? endpointName = null) where T : IResourceWithEndpoints { + ArgumentNullException.ThrowIfNull(builder); + endpointName = endpointName ?? "http"; return builder.WithHttpHealthCheckInternal( path: path, @@ -830,6 +925,8 @@ internal static IResourceBuilder WithHttpHealthCheckInternal(this IResourc /// public static IResourceBuilder WithHttpsHealthCheck(this IResourceBuilder builder, string? path = null, int? statusCode = null, string? endpointName = null) where T : IResourceWithEndpoints { + ArgumentNullException.ThrowIfNull(builder); + endpointName = endpointName ?? "https"; return builder.WithHttpHealthCheckInternal( path: path, @@ -887,6 +984,11 @@ public static IResourceBuilder WithCommand( IconVariant? iconVariant = null, bool isHighlighted = false) where T : IResource { + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(displayName); + ArgumentNullException.ThrowIfNull(executeCommand); + // Replace existing annotation with the same name. var existingAnnotation = builder.Resource.Annotations.OfType().SingleOrDefault(a => a.Name == name); if (existingAnnotation != null) diff --git a/tests/Aspire.Hosting.Containers.Tests/ContainerResourceTests.cs b/tests/Aspire.Hosting.Containers.Tests/ContainerResourceTests.cs index 57b1a3a5b87..4cbe5c7ce3d 100644 --- a/tests/Aspire.Hosting.Containers.Tests/ContainerResourceTests.cs +++ b/tests/Aspire.Hosting.Containers.Tests/ContainerResourceTests.cs @@ -185,7 +185,7 @@ public async Task EnsureContainerWithVolumesEmitsVolumes() builder.AddContainer("containerwithvolumes", "image/name") .WithVolume("myvolume", "/mount/here") .WithVolume("myreadonlyvolume", "/mount/there", isReadOnly: true) - .WithVolume(null! /* anonymous volume */, "/mount/everywhere"); + .WithVolume("/mount/everywhere"); using var app = builder.Build(); From 233f1fd6320d0e9bd9e8c7c7403de1e44c940f8f Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 29 Oct 2024 14:42:30 +0800 Subject: [PATCH 2/3] PR feedback, fixes --- src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs | 8 +++----- src/Aspire.Hosting/PublicAPI.Unshipped.txt | 2 ++ src/Aspire.Hosting/ResourceBuilderExtensions.cs | 2 +- .../ContainerResourceTests.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs index dae57249579..10e6d026321 100644 --- a/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ContainerResourceBuilderExtensions.cs @@ -56,10 +56,9 @@ public static IResourceBuilder AddContainer(this IDistributed /// The target path where the volume is mounted in the container. /// A flag that indicates if the volume should be mounted as read-only. /// The . - public static IResourceBuilder WithVolume(this IResourceBuilder builder, string name, string target, bool isReadOnly = false) where T : ContainerResource + public static IResourceBuilder WithVolume(this IResourceBuilder builder, string? name, string target, bool isReadOnly = false) where T : ContainerResource { ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(name); ArgumentNullException.ThrowIfNull(target); var annotation = new ContainerMountAnnotation(name, target, ContainerMountType.Volume, isReadOnly); @@ -145,10 +144,9 @@ public static IResourceBuilder WithImageTag(this IResourceBuilder build /// Builder for the container resource. /// Registry value. /// - public static IResourceBuilder WithImageRegistry(this IResourceBuilder builder, string registry) where T : ContainerResource + public static IResourceBuilder WithImageRegistry(this IResourceBuilder builder, string? registry) where T : ContainerResource { ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(registry); if (builder.Resource.Annotations.OfType().LastOrDefault() is { } existingImageAnnotation) { @@ -363,7 +361,7 @@ public static IResourceBuilder WithDockerfile(this IResourceBuilder bui var imageName = builder.GenerateImageName(); var annotation = new DockerfileBuildAnnotation(fullyQualifiedContextPath, fullyQualifiedDockerfilePath, stage); return builder.WithAnnotation(annotation, ResourceAnnotationMutationBehavior.Replace) - .WithImageRegistry(null!) + .WithImageRegistry(registry: null) .WithImage(imageName) .WithImageTag("latest"); } diff --git a/src/Aspire.Hosting/PublicAPI.Unshipped.txt b/src/Aspire.Hosting/PublicAPI.Unshipped.txt index 0c67fb7aaf8..1207d8eeae0 100644 --- a/src/Aspire.Hosting/PublicAPI.Unshipped.txt +++ b/src/Aspire.Hosting/PublicAPI.Unshipped.txt @@ -217,7 +217,9 @@ Aspire.Hosting.ApplicationModel.ResourceNotificationService.WaitForResourceAsync Aspire.Hosting.ApplicationModel.ResourceNotificationService.WaitForResourceAsync(string! resourceName, System.Collections.Generic.IEnumerable! targetStates, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task! static Aspire.Hosting.ApplicationModel.ResourceExtensions.TryGetAnnotationsIncludingAncestorsOfType(this Aspire.Hosting.ApplicationModel.IResource! resource, out System.Collections.Generic.IEnumerable? result) -> bool static Aspire.Hosting.ContainerResourceBuilderExtensions.WithContainerName(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string! name) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! +static Aspire.Hosting.ContainerResourceBuilderExtensions.WithImageRegistry(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string? registry) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.ContainerResourceBuilderExtensions.WithLifetime(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, Aspire.Hosting.ApplicationModel.ContainerLifetime lifetime) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! +static Aspire.Hosting.ContainerResourceBuilderExtensions.WithVolume(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string? name, string! target, bool isReadOnly = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.ParameterResourceBuilderExtensions.AddParameter(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, Aspire.Hosting.ApplicationModel.ParameterDefault! value, bool secret = false, bool persist = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.ParameterResourceBuilderExtensions.AddParameter(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, string! value, bool publishValueAsDefault = false, bool secret = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.ParameterResourceBuilderExtensions.AddParameter(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name, System.Func! valueGetter, bool publishValueAsDefault = false, bool secret = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! diff --git a/src/Aspire.Hosting/ResourceBuilderExtensions.cs b/src/Aspire.Hosting/ResourceBuilderExtensions.cs index 5fec612b95d..4c6e44572fb 100644 --- a/src/Aspire.Hosting/ResourceBuilderExtensions.cs +++ b/src/Aspire.Hosting/ResourceBuilderExtensions.cs @@ -985,7 +985,7 @@ public static IResourceBuilder WithCommand( bool isHighlighted = false) where T : IResource { ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(name); ArgumentNullException.ThrowIfNull(displayName); ArgumentNullException.ThrowIfNull(executeCommand); diff --git a/tests/Aspire.Hosting.Containers.Tests/ContainerResourceTests.cs b/tests/Aspire.Hosting.Containers.Tests/ContainerResourceTests.cs index 4cbe5c7ce3d..57b1a3a5b87 100644 --- a/tests/Aspire.Hosting.Containers.Tests/ContainerResourceTests.cs +++ b/tests/Aspire.Hosting.Containers.Tests/ContainerResourceTests.cs @@ -185,7 +185,7 @@ public async Task EnsureContainerWithVolumesEmitsVolumes() builder.AddContainer("containerwithvolumes", "image/name") .WithVolume("myvolume", "/mount/here") .WithVolume("myreadonlyvolume", "/mount/there", isReadOnly: true) - .WithVolume("/mount/everywhere"); + .WithVolume(null! /* anonymous volume */, "/mount/everywhere"); using var app = builder.Build(); From 08313acbcc08c773a75fbad755cb32cc7a293b8a Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 29 Oct 2024 14:44:59 +0800 Subject: [PATCH 3/3] Fixes --- src/Aspire.Hosting/PublicAPI.Shipped.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Aspire.Hosting/PublicAPI.Shipped.txt b/src/Aspire.Hosting/PublicAPI.Shipped.txt index dcd312f1fad..993f54cb560 100644 --- a/src/Aspire.Hosting/PublicAPI.Shipped.txt +++ b/src/Aspire.Hosting/PublicAPI.Shipped.txt @@ -438,10 +438,8 @@ static Aspire.Hosting.ContainerResourceBuilderExtensions.WithContainerRuntimeArg static Aspire.Hosting.ContainerResourceBuilderExtensions.WithContainerRuntimeArgs(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, System.Func! callback) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.ContainerResourceBuilderExtensions.WithEntrypoint(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string! entrypoint) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.ContainerResourceBuilderExtensions.WithImage(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string! image, string! tag = "latest") -> Aspire.Hosting.ApplicationModel.IResourceBuilder! -static Aspire.Hosting.ContainerResourceBuilderExtensions.WithImageRegistry(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string! registry) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.ContainerResourceBuilderExtensions.WithImageSHA256(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string! sha256) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.ContainerResourceBuilderExtensions.WithImageTag(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string! tag) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! -static Aspire.Hosting.ContainerResourceBuilderExtensions.WithVolume(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string! name, string! target, bool isReadOnly = false) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.ContainerResourceBuilderExtensions.WithVolume(this Aspire.Hosting.ApplicationModel.IResourceBuilder! builder, string! target) -> Aspire.Hosting.ApplicationModel.IResourceBuilder! static Aspire.Hosting.ContainerResourceExtensions.GetContainerResources(this Aspire.Hosting.ApplicationModel.DistributedApplicationModel! model) -> System.Collections.Generic.IEnumerable! static Aspire.Hosting.ContainerResourceExtensions.IsContainer(this Aspire.Hosting.ApplicationModel.IResource! resource) -> bool