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 command annotations and wire up #5538

Merged
merged 4 commits into from
Sep 20, 2024

Conversation

JamesNK
Copy link
Member

@JamesNK JamesNK commented Sep 4, 2024

Fixes #1410 (cleaned up test while I was here)
Fixes #295

Changes in PR:

  • Add resource command annotations API
  • Resource commands talking to host and invoking annotation callbacks
  • Start/stop/restart added to containers/projects/executable
  • Start/stop/restart unit and functional tests

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes - will schedule
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?
Microsoft Reviewers: Open in CodeFlow

@mitchdenny
Copy link
Member

Looking good overall. This was pretty close to what I was envisaging. Left a few comments to discuss.

@JamesNK
Copy link
Member Author

JamesNK commented Sep 5, 2024

Made a bunch of changes and applied PR feedback.

I think this is close to being good. (Ignore lack of API xml comments and the fact that start/stop/restart don't actually do anything yet 😄)

Base automatically changed from jamesnk/commands-glow-up to main September 6, 2024 10:38
@JamesNK JamesNK force-pushed the jamesnk/command-annotations-and-wireup branch from c1cfac8 to 45b8706 Compare September 9, 2024 00:30
@JamesNK JamesNK force-pushed the jamesnk/command-annotations-and-wireup branch from 8f49cda to 9996ae5 Compare September 10, 2024 00:10
Comment on lines 48 to 87
await executor.StopResourceAsync(context.ResourceName, context.CancellationToken).ConfigureAwait(false);
await executor.StartResourceAsync(context.ResourceName, context.CancellationToken).ConfigureAwait(false);
Copy link
Member

Choose a reason for hiding this comment

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

Does this work? Start/Stop in quick succession?

Copy link
Member Author

@JamesNK JamesNK Sep 11, 2024

Choose a reason for hiding this comment

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

I talked with @karolz-ms about restarting containers, and it sounds like that work might not get done for 9 because wait for is prioritized. Therefor to restart both containers and executables from the app host, we'll delete the resource and then recreate it.

That means the StopResourceAsync may or may not be needed here. Do we want to gracefully stop a resource before it starting it up again? Or do we just delete and recreate it? Delete+create would be faster.

@JamesNK JamesNK force-pushed the jamesnk/command-annotations-and-wireup branch from e4cb2b8 to 5c17fb3 Compare September 12, 2024 05:24
string type,
string displayName,
Func<UpdateCommandStateContext, ResourceCommandState> updateState,
Func<ExecuteCommandContext, Task> executeCommand,
Copy link
Member

Choose a reason for hiding this comment

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

I was thinking about whether we should return something like the equivalent of Task<IActionResult> (not actual IActionResult just trying to conjure the pattern). The idea is that that no only does the task being awaited some indication of success or failure, but it also allows us to provide the dashboard with additional information that can be used to control what happens.

For example we might want to provide some kind of call to action button that the user can click to navigate away to a page - or in some cases a redirect action which would immediately navigate the page away to a specific URL. For example if someone adds the WithPgAdmin(...) command then all the databases on that server would get a command View in PgAdmin that if they clicked on it would be deep linked to that database in the PgAdmin instance that the database is mapped to.

Other options for projects might be doing things like scanning the project directory for .http files and adding a command for each .http file so that when it is clicked the .http request is executed and the JSON result is displayed in a JSON viewer in the screen.

We don't necessarily need all these options straightaway but given that there are some useful scenarios here making this return a more fully fledged object might be an idea. Then we could extend it overtime with more scenarios.

@JamesNK
Copy link
Member Author

JamesNK commented Sep 18, 2024

However, when applied more broadly the resource state snapshot is not the only input into deciding whether a command might be enabled or not. For example you might want to query the state of the resource directly to determine whether a command should be available. This would require resolving the connection string to the resource which is an async operation.

Here is the problem with what you're saying: the update method is only triggered when the resource snapshot updates. If you need to talk to the database to figure out the state, you can only do that if something updates the resource snapshot. The resource might be in a happy Running state since it starts up and the command state methods are never called.

For example, there is a command to perform an EF migration. It has an update state method that queries the DB to see if the command is enabled or disabled. The command could be enabled and some other tool could perform the migration - so the command should be disabled - but the command stays enabled because the resource snapshot doesn't update.

The way this could work instead is when the resource starts there is a background task that queries the database (or file, or web service, etc) every 10 seconds to figure out whether the command is valid or not. That task uses ResourceNotificationService to set a property on the resource snapshot. The command update method then looks at that property to figure out whether it is enabled or not.

We probably shouldn't be polling this multiple times a second anyway (its UX behind a context menu). If we are concerned about the overhead of running the state updates for commands then we can always poll at a lower interval.

This isn't from polling. The methods are re-evaluated when the resource state updates, which is pushed from the notification service. A resource starting up or shutting down has half a dozen rapid resource state updates as the state and endpoints change.

If there is a command state update method that does something like query a web service, and the network is down, then it could take seconds to time out. Resource updates in Asoure are now going to be slowed down while waiting for that timeout, multipled by the number of times it is called.

@JamesNK JamesNK force-pushed the jamesnk/command-annotations-and-wireup branch from 3987b04 to d7fefb8 Compare September 19, 2024 02:25
@JamesNK JamesNK changed the title [WIP] Add command annotations and wire up Add command annotations and wire up Sep 19, 2024
@JamesNK JamesNK force-pushed the jamesnk/command-annotations-and-wireup branch from e9babf4 to 97ac713 Compare September 19, 2024 08:41
@JamesNK JamesNK marked this pull request as ready for review September 19, 2024 08:41
@@ -2,12 +2,12 @@
@using Aspire.Dashboard.Model
@using Microsoft.FluentUI.AspNetCore.Components

@foreach (var highlightedCommand in Commands.Where(c => c.IsHighlighted))
@foreach (var highlightedCommand in Commands.Where(c => c.IsHighlighted && c.State != CommandViewModelState.Hidden))
Copy link
Member

@mitchdenny mitchdenny Sep 19, 2024

Choose a reason for hiding this comment

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

Should there be a maximum number of highlighted commands? Or rather only the first N highlighted commands?

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe. It's easy to change if necessary.

@@ -49,7 +49,7 @@ protected override void OnParametersSet()
OnClick = OnConsoleLogs.InvokeAsync
});

var menuCommands = Commands.Where(c => !c.IsHighlighted).ToList();
var menuCommands = Commands.Where(c => !c.IsHighlighted && c.State != CommandViewModelState.Hidden).ToList();
Copy link
Member

Choose a reason for hiding this comment

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

Should the context menu show all commands regardless of whether they are highlighted or not?

Copy link
Member Author

Choose a reason for hiding this comment

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

Dunno. It's easy to change.

@@ -33,7 +33,7 @@
Class="severity-icon" />
}
}
else if (Resource.IsStartingOrBuildingOrWaiting())
else if (Resource.IsUnusableTransitoryState())
Copy link
Member

Choose a reason for hiding this comment

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

It's a race with #5770 :)

Copy link
Member

@mitchdenny mitchdenny left a comment

Choose a reason for hiding this comment

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

LGTM. Some minor points of feedback that can be follow up PRs if necessary.

@JamesNK JamesNK enabled auto-merge (squash) September 19, 2024 14:41
@JamesNK
Copy link
Member Author

JamesNK commented Sep 20, 2024

/azp run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

if (!resourceNotFound)
{
// Limit polling to 5 attempts to avoid hanging with an infinite loop.
for (var i = 0; i < 5; i++)
Copy link
Member

Choose a reason for hiding this comment

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

Why don't you use polly 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

I've used polly zero times and I've used for loops a million times 😋

Next time I make start/stop/restart changes in this area I'll try it out.

Copy link
Member

Choose a reason for hiding this comment

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

It's in this same file

Copy link
Member Author

Choose a reason for hiding this comment

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

Since the build is failing anyway, changed to polly.

Observation: I haven't used polly before, but should the polly options and pipelines be cached? They're created every run right now. Might not be necessary.

@JamesNK
Copy link
Member Author

JamesNK commented Sep 20, 2024

/azp run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@JamesNK JamesNK merged commit a5b3bcf into main Sep 20, 2024
11 checks passed
@JamesNK JamesNK deleted the jamesnk/command-annotations-and-wireup branch September 20, 2024 07:19
@davidfowl davidfowl added this to the 9.0 milestone Sep 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants