Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable devcontainers in repo. #6491

Merged
merged 16 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
{
"name": "C# (.NET)",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/dotnet:1-9.0",
"features": {
"ghcr.io/devcontainers/features/azure-cli:1": {},
"ghcr.io/azure/azure-dev/azd:0": {},
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved
"ghcr.io/devcontainers/features/docker-in-docker": {},
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved
"ghcr.io/devcontainers/features/dotnet": {
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved
"additionalVersions": [
"8.0.403"
]
}
},

"hostRequirements": {
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved
"cpus": 8,
"memory": "32gb",
"storage": "64gb"
},

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved
15887,
5180,
7024,
15551,
33803,
5350,
41567,
15306
],
"portsAttributes": {
"5180": {
"label": "WaitFor Playground: ApiService",
"protocol": "http"
},
"5350": {
"label": "Redis Playground: Api Service"
},
"7024": {
"label": "WaitFor Playground: Frontend",
"protocol": "https"
},
"15306": {
"label": "Redis Playground: App Host"
},
"15551": {
"label": "WaitFor Playground: PGAdmin",
"protocol": "http"
},
"15887": {
"label": "WaitFor Playground: AppHost",
"protocol": "https"
},
"33803": {
"label": "Redis Playground: Redis Commander"
},
"41567": {
"label": "Redis Playground: Redis Insight"
}
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved
},

// Use 'postCreateCommand' to run commands after the container is created.
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csdevkit",
"ms-azuretools.vscode-bicep",
"ms-azuretools.azure-dev"
],
"settings": {
"remote.autoForwardPorts": false,
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved
"dotnet.defaultSolution": "Aspire.sln"
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved
}
}
},
"onCreateCommand": "dotnet restore",
"postStartCommand": "dotnet dev-certs https --trust"
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved

// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
4 changes: 2 additions & 2 deletions playground/Redis/Redis.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

var redis = builder.AddRedis("redis")
.WithDataVolume()
.WithRedisCommander()
.WithRedisInsight();
.WithRedisCommander(c => c.WithHostPort(33803))
.WithRedisInsight(c => c.WithHostPort(41567));

var garnet = builder.AddGarnet("garnet")
.WithDataVolume();
Expand Down
5 changes: 4 additions & 1 deletion playground/waitfor/WaitForSandbox.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
.WithPasswordAuthentication()
.RunAsContainer(c =>
{
c.WithPgAdmin();
c.WithPgAdmin(c =>
{
c.WithHostPort(15551);
mitchdenny marked this conversation as resolved.
Show resolved Hide resolved
});
})
.AddDatabase("db");

Expand Down
11 changes: 11 additions & 0 deletions src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Postgres;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Aspire.Hosting;
Expand Down Expand Up @@ -333,6 +334,16 @@ private static void SetPgAdminEnvironmentVariables(EnvironmentCallbackContext co
// You need to define the PGADMIN_DEFAULT_EMAIL and PGADMIN_DEFAULT_PASSWORD or PGADMIN_DEFAULT_PASSWORD_FILE environment variables.
context.EnvironmentVariables.Add("PGADMIN_DEFAULT_EMAIL", "[email protected]");
context.EnvironmentVariables.Add("PGADMIN_DEFAULT_PASSWORD", "admin");

// When running in the context of Codespaces we need to set some additional environment
// varialbes so that PGAdmin will trust the forwarded headers that Codespaces port
// forwarding will send.
var config = context.ExecutionContext.ServiceProvider.GetRequiredService<IConfiguration>();
if (context.ExecutionContext.IsRunMode && config.GetValue<bool>("CODESPACES", false))
{
context.EnvironmentVariables["PGADMIN_CONFIG_PROXY_X_HOST_COUNT"] = "1";
context.EnvironmentVariables["PGADMIN_CONFIG_PROXY_X_PREFIX_COUNT"] = "1";
}
}

/// <summary>
Expand Down
66 changes: 66 additions & 0 deletions src/Aspire.Hosting/Codespaces/CodespacesOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;

namespace Aspire.Hosting.Codespaces;

/// <summary>
/// GitHub Codespaces configuration values.
/// </summary>
internal class CodespacesOptions
{
/// <summary>
/// When set to true, the app host is running in a GitHub Codespace.
/// </summary>
/// <remarks>
/// Maps to the CODESPACE environment variable.
/// </remarks>
public bool IsCodespace { get; set; }

/// <summary>
/// When set it is the domain suffix used when port forwarding services hosted on the Codespace.
/// </summary>
/// <remarks>
/// Maps to the GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN environment variable.
/// </remarks>
[MemberNotNullWhen(true, nameof(IsCodespace))]
public string? PortForwardingDomain { get; set; }

/// <summary>
/// When set it is the name of the GitHub Codespace in which the app host is running.
/// </summary>
/// <remarks>
/// Maps to the CODESPACE_NAME environment variable.
/// </remarks>
[MemberNotNullWhen(true, nameof(IsCodespace))]
public string? CodespaceName { get; set; }
}

internal class ConfigureCodespacesOptions(IConfiguration configuration) : IConfigureOptions<CodespacesOptions>
{
private const string CodespacesEnvironmentVariable = "CODESPACES";
private const string CodespaceNameEnvironmentVariable = "CODESPACE_NAME";
private const string GitHubCodespacesPortForwardingDomain = "GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN";

private string GetRequiredCodespacesConfigurationValue(string key)
{
ArgumentNullException.ThrowIfNullOrEmpty(key);
return configuration.GetValue<string>(key) ?? throw new DistributedApplicationException($"Codespaces was detected but {key} environment missing.");
}

public void Configure(CodespacesOptions options)
{
if (!configuration.GetValue<bool>(CodespacesEnvironmentVariable, false))
{
options.IsCodespace = false;
return;
}

options.IsCodespace = true;
options.PortForwardingDomain = GetRequiredCodespacesConfigurationValue(GitHubCodespacesPortForwardingDomain);
options.CodespaceName = GetRequiredCodespacesConfigurationValue(CodespaceNameEnvironmentVariable);
}
}
21 changes: 4 additions & 17 deletions src/Aspire.Hosting/Codespaces/CodespacesUrlRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,22 @@

using System.Collections.Immutable;
using Aspire.Hosting.ApplicationModel;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Aspire.Hosting.Codespaces;

internal sealed class CodespacesUrlRewriter(ILogger<CodespacesUrlRewriter> logger, IConfiguration configuration, ResourceNotificationService resourceNotificationService) : BackgroundService
internal sealed class CodespacesUrlRewriter(ILogger<CodespacesUrlRewriter> logger, IOptions<CodespacesOptions> options, ResourceNotificationService resourceNotificationService) : BackgroundService
{
private const string CodespacesEnvironmentVariable = "CODESPACES";
private const string CodespaceNameEnvironmentVariable = "CODESPACE_NAME";
private const string GitHubCodespacesPortForwardingDomain = "GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN";

private string GetRequiredCodespacesConfigurationValue(string key)
{
ArgumentNullException.ThrowIfNullOrEmpty(key);
return configuration.GetValue<string>(key) ?? throw new DistributedApplicationException($"Codespaces was detected but {key} environment missing.");
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
if (!configuration.GetValue<bool>(CodespacesEnvironmentVariable, false))
if (!options.Value.IsCodespace)
{
logger.LogTrace("Not running in Codespaces, skipping URL rewriting.");
return;
}

var gitHubCodespacesPortForwardingDomain = GetRequiredCodespacesConfigurationValue(GitHubCodespacesPortForwardingDomain);
var codespaceName = GetRequiredCodespacesConfigurationValue(CodespaceNameEnvironmentVariable);

do
{
try
Expand All @@ -58,7 +45,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
// which is typically ".app.github.dev". The VSCode instance is typically
// hosted at codespacename.github.dev whereas the forwarded ports
// would be at codespacename-port.app.github.dev.
Url = $"{uri.Scheme}://{codespaceName}-{uri.Port}.{gitHubCodespacesPortForwardingDomain}{uri.AbsolutePath}"
Url = $"{uri.Scheme}://{options.Value.CodespaceName}-{uri.Port}.{options.Value.PortForwardingDomain}{uri.AbsolutePath}"
};

remappedUrls.Add(originalUrlSnapshot, newUrlSnapshot);
Expand Down
1 change: 1 addition & 0 deletions src/Aspire.Hosting/DistributedApplicationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options)
_innerBuilder.Services.AddSingleton<IKubernetesService, KubernetesService>();

// Codespaces
_innerBuilder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<CodespacesOptions>, ConfigureCodespacesOptions>());
_innerBuilder.Services.AddHostedService<CodespacesUrlRewriter>();

Eventing.Subscribe<BeforeStartEvent>(BuiltInDistributedApplicationEventSubscriptionHandlers.InitializeDcpAnnotations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ public class CodespacesUrlRewriterTests(ITestOutputHelper testOutputHelper)
public async Task VerifyUrlsRewriterStopsWhenNotInCodespaces()
{
using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper);

// Explicitly disable codespace behavior for this test.
builder.Configuration["CODESPACES"] = "false";

builder.Services.AddLogging(logging =>
{
logging.AddFakeLogging();
Expand Down
Loading