-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Refactor workload set and workload install/update logic #39991
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
Refactor workload set and workload install/update logic #39991
Conversation
68f2114
to
43f7012
Compare
43f7012
to
58bf7f6
Compare
{ | ||
Directory.Delete(dir); | ||
directoriesDeleted++; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a concern that we'll get stuck in this loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really—we'll start out a finite number of directories deep, and that provides an upper limit to how many times it'll run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to use this as part of cleaning up the workload install records, but I didn't want it to delete directories further than a certain level. It probably would have been OK to delete empty directories and they would have been recreated if needed, but it felt safer to me to limit it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm most of the way through this but want to give it another look later. Appreciate the refactoring!
_workloadInstaller.RemoveManifestsFromInstallState(_sdkFeatureBand); | ||
} | ||
|
||
_workloadInstaller.AdjustWorkloadSetInInstallState(_sdkFeatureBand, string.IsNullOrWhiteSpace(_workloadSetVersion) ? null : _workloadSetVersion); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means we're removing the workload set from the install state if it had been pinned previously even if we're doing an install. As in your todo, we need to leave it if the workload set version is pinned in the install state for install. I did that in my PR here, though it might be cleaner to handle this with the 'resolvedWorkloadSetVersion' you made.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolvedWorkloadSetVersion wasn't what I'd thought it was, so ignore that part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also be sure we handle global.json properly.
} | ||
// All installation records for the manifest were removed, so we can delete the manifest | ||
_reporter.WriteLine(string.Format(LocalizableStrings.DeletingWorkloadManifest, manifestId, $"{manifestVersion}/{manifestFeatureBand}")); | ||
var manifestPath = Path.Combine(GetManifestInstallDirForFeatureBand(manifestFeatureBand.ToString()), manifestId.ToString(), manifestVersion.ToString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we have to delete the install records anymore? Wouldn't leaving old ones behind mean we wouldn't be able to delete it if it's reinstalled later?
{ | ||
manifestsToUpdate = InstallWorkloadSet(context); | ||
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetPath), "default.json"); | ||
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) && File.Exists(installStateFilePath) && InstallStateContents.FromString(File.ReadAllText(installStateFilePath)).Manifests is not null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) && File.Exists(installStateFilePath) && InstallStateContents.FromString(File.ReadAllText(installStateFilePath)).Manifests is not null) | |
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) && InstallStateContents.FromPath(installStateFilePath)?.Manifests is not null) |
This is what FromPath is for 🙂
} | ||
if (_skipManifestUpdate) | ||
{ | ||
_workloadInstaller.InstallWorkloads(workloadIds, _sdkFeatureBand, context, offlineCache); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this raises an interesting question:
If you ask for a workload set (via global.json, say) and you also specify to skip manifest updates, what should our response be? I don't think "install the latest version" makes sense because that would violate our workload set, but we can't update other manifests, so there's no point in aligning with the workload set. Perhaps that should be an error?
} | ||
|
||
// Write workload installation records | ||
var recordRepo = _workloadInstaller.GetWorkloadInstallationRecordRepository(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to do this part before we install more workloads? Wouldn't this just be empty?
} | ||
}); | ||
|
||
TryRunGarbageCollection(_workloadInstaller, Reporter, Verbosity, workloadSetVersion => _workloadResolverFactory.CreateForWorkloadSet(_dotnetPath, _sdkVersion.ToString(), _userProfileDir, workloadSetVersion), offlineCache); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to refresh workload manifests before we run GC.
string[] sections = setVersion.Split(new char[] { '-', '+' }, 2); | ||
string versionCore = sections[0]; | ||
string preReleaseOrBuild = sections.Length > 1 ? sections[1] : null; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you doing all this manually instead of using NugetVersion's parsing? I'm having a little trouble figuring out what exactly you're doing here.
/cc: @joeloff
@@ -194,6 +214,17 @@ public static void AdvertiseWorkloadUpdates() | |||
} | |||
} | |||
|
|||
public string GetAdvertisedWorkloadSetVersion() | |||
{ | |||
var advertisedPath = GetAdvertisingManifestPath(_sdkFeatureBand, new ManifestId(WorkloadSetManifestId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's only ever one advertising manifest for workload sets? I don't remember for sure.
@@ -3,6 +3,7 @@ | |||
|
|||
namespace Microsoft.NET.Sdk.WorkloadManifestReader | |||
{ | |||
// TODO: Do we need this class, or the existing version information anymore now that workload manifest are side by side? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only time I can think of that it would be helpful is if we're rolling back to a pinned version of something, but I don't think we handle that 100% correctly now, so we can probably dispense with it.
{ | ||
manifestsToUpdate = InstallWorkloadSet(context, resolvedWorkloadSetVersion); | ||
} | ||
else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could probably make this whole thing a ternary; I only didn't because the return value was different for workload sets.
_workloadInstaller.RemoveManifestsFromInstallState(_sdkFeatureBand); | ||
} | ||
|
||
_workloadInstaller.AdjustWorkloadSetInInstallState(_sdkFeatureBand, string.IsNullOrWhiteSpace(_workloadSetVersion) ? null : _workloadSetVersion); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolvedWorkloadSetVersion wasn't what I'd thought it was, so ignore that part.
_workloadInstaller.RemoveManifestsFromInstallState(_sdkFeatureBand); | ||
} | ||
|
||
_workloadInstaller.AdjustWorkloadSetInInstallState(_sdkFeatureBand, string.IsNullOrWhiteSpace(_workloadSetVersion) ? null : _workloadSetVersion); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also be sure we handle global.json properly.
workloadSet.IsBaselineWorkloadSet = true; | ||
} | ||
|
||
workloadSet.Version = workloadSetVersion; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels a little weird to assert that this random workload set we found is the version specified in the argument to this method...maybe move this to be next to where we actually figured out what workload set version we wanted? Note that I think if we request version x from NuGet, we might get something else that's close.
58bf7f6
to
7099ad0
Compare
599a15a
to
2438d99
Compare
8a4f1db
to
d6f4293
Compare
d5658cd
to
b5a26b2
Compare
c30f7b4
to
ce07ca3
Compare
ce07ca3
to
4bb9d50
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't reviewed tests yet
src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs
Outdated
Show resolved
Hide resolved
src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs
Show resolved
Hide resolved
{ | ||
RunInNewTransaction(context => | ||
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetPath), "default.json"); | ||
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't there be more conditions here for other reasons to require a manifest update?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clarification: consider adding string.isnull checks for --version inputs and global.json version inputs to be complete
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping on this one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you saying that you think we should skip manifest update if --sdk-version
is specified or if there's a workload version in global.json? In those cases we do want to call the UpdateWorkloadManifests
method. That method is responsible for respecting the version from --sdk-version
or global.json. If a version was specified in either of those ways, then it will install those versions.
So I think the logic is correct, let me know if there's a case I'm missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should skip manifest update if
--sdk-version
is specified or if there's a workload version in global.json
The opposite: I think we should never skip manifest updates if either of those is specified. This if statement currently says that if rollback is not specified, and there is something pinned in the install state file, then we do skip manifest updates, which is wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) && | |
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) && | |
!SpecifiedWorkloadSetVersionOnCommandLine && | |
!SpecifiedWorkloadSetVersionInGlobalJson && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The explanation as I understand it is that forgind is saying that if we already have an install state, this logic will skip workload updates even if you have a global.json or --version specified (which could be different than what is in the install state). So I think you are agreeing with each other. Daniel, I think you're implying that UpdateWorkloadManifests would end up getting called for those cases which I think is true the first time a customer does it but any subsequent times after having an install state, I think forgind may be right unless I'm missing a code path that calls it elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I think I get it now. I will try to add a test to validate the behavior and then update the logic. I'm not sure how quickly I'll be able to do that though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be fixed now
|
||
RunInNewTransaction(context => | ||
{ | ||
if (!_skipManifestUpdate) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't figured out why yet, but skipping UpdateWorkloadManifests here makes me uncomfortable.
…ors for combinations with skip manifest update
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still haven't gotten to tests yet, but I think the code looks good other than these 6. (I mostly just care about the fourth and sixth.)
src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx
Outdated
Show resolved
Hide resolved
src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx
Outdated
Show resolved
Hide resolved
src/Cli/dotnet/commands/dotnet-workload/install/WorkloadGarbageCollector.cs
Show resolved
Hide resolved
src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs
Show resolved
Hide resolved
{ | ||
RunInNewTransaction(context => | ||
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetPath), "default.json"); | ||
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping on this one
throw new NotImplementedException(); | ||
} | ||
|
||
[Fact] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have a test that tries to install from another feature band?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up testing this incidentally, as I switched to using 8.0.302 instead of an 8.0.300-preview.0 build. That's how I found the issue with cross-band workload set GC.
Some of the VM tests are now failing though because of the band mismatch (because when you do a workload set update on an 8.0.300 band SDK, it won't install 8.0.300-preview.0 workload sets, which is what the tests are expecting).
I will probably try to sort through that and get all the tests working separately from this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just thinking perhaps we could have another test that just throws a NotImplementedException (that we'll later make real) to explicitly do cross band install/update, but if it's covered decently well by the 8.0.300-preview tests, that works for me.
…l.json or command-line specified workload set version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Looks good to me! 🚢
@@ -294,6 +293,52 @@ public void InstallWithVersionAndSkipManifestUpdate() | |||
.And.HaveStdErrContaining("--sdk-version"); | |||
} | |||
|
|||
[Fact] | |||
public void InstallWithVersionWhenPinned() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the perfect test for testing this change. Simple, elegant, and it works. It'd be nice to make it run in CI, but this should be fine.
Merging per offline discussions as this is now signed off. We'll get it merged into VS main for testing. |
WorkloadSet.FromWorkloadSetFolder