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

Add WithPgWeb #5098

Merged
merged 21 commits into from
Aug 20, 2024
Merged

Add WithPgWeb #5098

merged 21 commits into from
Aug 20, 2024

Conversation

Alirexaa
Copy link
Contributor

@Alirexaa Alirexaa commented Jul 28, 2024

In this PR, we add pgweb (https://github.com/sosedoff/pgweb) a web-based admin dashboard for PostgreSQL.

using WithPgWeb, We collect all registered PostgresDatabaseResource instances and create a configuration file per instance, then bind the location of these files to the pgweb container bookmark directory. for more information about pgweb bookmark you can see https://github.com/sosedoff/pgweb/wiki/Server-Connection-Bookmarks.

We used the Eventing API and subscribed AfterEndpointsAllocatedEvent to create configuration files.

We used pgweb connect API for functional testing to ensure pgweb is configured correctly.

var testConnectionApiUrl = $"{pgWebEndpoint.Scheme}://{pgWebEndpoint.Host}:{pgWebEndpoint.Port}/api/connect";

var pipeline = new ResiliencePipelineBuilder().AddRetry(new Polly.Retry.RetryStrategyOptions
{
    Delay = TimeSpan.FromSeconds(2),
    MaxRetryAttempts = 10,
}).Build();

await pipeline.ExecuteAsync(async (ct) =>
{
    var httpContent = new MultipartFormDataContent
    {
        { new StringContent(dbName), "bookmark_id" }
    };

    client.DefaultRequestHeaders.Add("x-session-id", Guid.NewGuid().ToString());

    var response = await client.PostAsync(testConnectionApiUrl, httpContent, ct)
        .ConfigureAwait(false);

    response.EnsureSuccessStatusCode();
}, cts.Token).ConfigureAwait(false);

close: #3081

Microsoft Reviewers: Open in CodeFlow

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-integrations Issues pertaining to Aspire Integrations packages label Jul 28, 2024
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jul 28, 2024
@mitchdenny mitchdenny marked this pull request as ready for review July 29, 2024 03:18
Aspire.sln Outdated Show resolved Hide resolved
@mitchdenny
Copy link
Member

@Alirexaa this is looking pretty good. A few minor things. We'll need a docs issue/PR filed for this as well (probably just updating the existing doc page for Postgres).

@Alirexaa
Copy link
Contributor Author

Alirexaa commented Jul 29, 2024

@mitchdenny besides these things, this PR needs more work to do, right now bookmarks populate in pgweb but when selecting one of those to connect, connection refuse occur.
Also need to write some tests

@Alirexaa Alirexaa changed the title WIP: Add WithPgWeb Add WithPgWeb Aug 8, 2024
@Alirexaa
Copy link
Contributor Author

Alirexaa commented Aug 8, 2024

To pass all tests we need to import docker.io/sosedoff/pgweb/latest to netaspireci.azurecr.io

@Alirexaa
Copy link
Contributor Author

Alirexaa commented Aug 9, 2024

@eerhardt could you please import docker.io/sosedoff/pgweb/latest to make ci green?

@@ -11,4 +11,7 @@ internal static class PostgresContainerImageTags
public const string PgAdminRegistry = "docker.io";
public const string PgAdminImage = "dpage/pgadmin4";
public const string PgAdminTag = "8.8";
public const string PgWebRegistry = "docker.io";
public const string PgWebImage = "sosedoff/pgweb";
public const string PgWebTag = "latest";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now we use the 0.15.0 tag,Could you please import docker.io/sosedoff/pgweb:0.15.0?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can do that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eerhardt already did it!

src/Aspire.Hosting.PostgreSQL/PgWebConfigWriterHook.cs Outdated Show resolved Hide resolved
{
var adminResource = appModel.Resources.OfType<PgWebContainerResource>().Single();
var serverFileMount = adminResource.Annotations.OfType<ContainerMountAnnotation>().Single(v => v.Target == "/.pgweb/bookmarks");
var postgresInstances = appModel.Resources.OfType<PostgresDatabaseResource>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to do this for all PostgresDatabaseResource instances? Or just the ones that had .WithPgWeb() called on them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to introduce a new annotation to make a relation between PgWebContainerResource and PostgresDatabaseResource or maybe something like this

public sealed class PgWebContainerResource(string name,PostgresDatabaseResource postgresDatabaseResource) : ContainerResource(name)
{
   public PostgresDatabaseResource PostgresDatabaseResource { get; } = postgresDatabaseResource;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eerhardt, what about this?
We have same behavior for pgadmin.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess if the behavior is the same as pgadmin, then I'm fine. If we feel we should have a different behavior for both - log an issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have an open issue for that. see #2060.

src/Aspire.Hosting.PostgreSQL/PostgresBuilderExtensions.cs Outdated Show resolved Hide resolved
builder.ApplicationBuilder.Services.TryAddLifecycleHook<PgWebConfigWriterHook>();

containerName ??= $"{builder.Resource.Name}-pgweb";
var dir = Directory.CreateTempSubdirectory().FullName;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this directory get cleaned up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have a hook or event when a resource stopped/finished?

I think we should add one API for that in IDistributedApplicationLifecycleHook

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually a problem right now. @karolz-ms interested in your thoughts here. We sometimes drop temporary files in disk and I'd like to be able to try and ensure that they are cleaned up. We can probably have some kind of temporary file register that goes and does clean up but its highly likely that some of those files/directories are going to be locked because things like containers are spinning down.

This might be one of those things that DCP could help with?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think DCP would be a good place for logic that deals with "cleanup" tasks like removing temporary log files.

There are probably many ways to do this, but the simplest one that will work today, is to put them inside the DCP session folder (which the app host runtime knows about). That folder is automatically deleted when DCP is shutting down. Just make sure the name is reasonably unique when creating the file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(we might be missing an API for the extensions that will tell them what the "temporary folder for the current application run" is)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do that too. Feel free to open an issue and we can add it to our backlog for DCP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is our approach for cleanup temp files for this PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mitchdenny - would this be a good usage of the new Eventing API? We could have an event that fires when the resource is shut down.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is our approach for cleanup temp files for this PR?

Since we already have the same issue with pgadmin:

.WithBindMount(Path.GetTempFileName(), "/pgadmin4/servers.json")
.ExcludeFromManifest();
builder.ApplicationBuilder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>((e, ct) =>
{
var serverFileMount = pgAdminContainer.Annotations.OfType<ContainerMountAnnotation>().Single(v => v.Target == "/pgadmin4/servers.json");
var postgresInstances = builder.ApplicationBuilder.Resources.OfType<PostgresServerResource>();
var serverFileBuilder = new StringBuilder();
using var stream = new FileStream(serverFileMount.Source!, FileMode.Create);

I'd be fine with continuing here to just create temp files and leave them, for now, until we get the clean up capability.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes ... although it wouldn't be 100%. If someone kills the process via the debugger (most common scenario) I don't think we guarantee that this is run. But better than nothing I guess.

tests/Aspire.Hosting.PostgreSQL.Tests/AddPostgresTests.cs Outdated Show resolved Hide resolved
{
var adminResource = appModel.Resources.OfType<PgWebContainerResource>().Single();
var serverFileMount = adminResource.Annotations.OfType<ContainerMountAnnotation>().Single(v => v.Target == "/.pgweb/bookmarks");
var postgresInstances = appModel.Resources.OfType<PostgresDatabaseResource>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess if the behavior is the same as pgadmin, then I'm fine. If we feel we should have a different behavior for both - log an issue.

@radical
Copy link
Member

radical commented Aug 15, 2024

Can you please add some information in PR description, maybe even just links to pgweb?

@eerhardt
Copy link
Member

@eerhardt could you please import docker.io/sosedoff/pgweb/latest to make ci green?

I've pushed it to the netaspireci.azurecr.io registry. However, I don't believe the test will pass because the CI machines don't have Docker Desktop installed, which means there isn't network communication between the containers.

@Alirexaa
Copy link
Contributor Author

@eerhardt could you please import docker.io/sosedoff/pgweb/latest to make ci green?

I've pushed it to the netaspireci.azurecr.io registry. However, I don't believe the test will pass because the CI machines don't have Docker Desktop installed, which means there isn't network communication between the containers.

What can I do for that?

@eerhardt
Copy link
Member

What can I do for that?

For now we've been disabling the test in CI until the problem can be resolved.

@radical
Copy link
Member

radical commented Aug 16, 2024

/azp run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for this contribution!

@eerhardt eerhardt enabled auto-merge (squash) August 19, 2024 23:07
@eerhardt eerhardt merged commit a2e9afa into dotnet:main Aug 20, 2024
11 checks passed
@Alirexaa Alirexaa deleted the WithPgweb branch August 20, 2024 06:36
@github-actions github-actions bot locked and limited conversation to collaborators Sep 19, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-integrations Issues pertaining to Aspire Integrations packages community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Consider adding support for pgweb as an alternative to pgAdmin
6 participants