private IEnumerable <string> GetExpectedPackInstallRecords(SdkFeatureBand sdkFeatureBand) { var installedWorkloads = GetInstalledWorkloads(sdkFeatureBand); return(installedWorkloads .SelectMany(workload => _workloadResolver.GetPacksInWorkload(workload)) .Select(pack => _workloadResolver.TryGetPackInfo(pack)) .Select(packInfo => GetPackInstallRecordPath(packInfo, sdkFeatureBand))); }
public IEnumerable <PackInfo> GetInstalledPacks(SdkFeatureBand sdkFeatureBand) { var installedPacksDir = Path.Combine(_workloadMetadataDir, _installedPacksDir, "v1"); return(Directory.GetDirectories(installedPacksDir) .Select(packIdPath => Path.GetFileName(packIdPath)) .Select(packId => _workloadResolver.TryGetPackInfo(packId)) .Where(pack => pack != null)); }
private IEnumerable <string> GetExpectedPackInstallRecords(SdkFeatureBand sdkFeatureBand) { var installedWorkloads = _installationRecordRepository.GetInstalledWorkloads(sdkFeatureBand); return(installedWorkloads .SelectMany(workload => _workloadResolver.GetPacksInWorkload(workload.ToString())) .Select(pack => _workloadResolver.TryGetPackInfo(pack)) .Where(pack => pack != null) .Select(packInfo => GetPackInstallRecordPath(packInfo, sdkFeatureBand))); }
private IEnumerable <PackInfo> GetUpdatablePacks(IWorkloadPackInstaller installer) { var currentFeatureBand = new SdkFeatureBand(_sdkVersion); var workloads = GetUpdatableWorkloads(); var updatedPacks = workloads.SelectMany(workloadId => _workloadResolver.GetPacksInWorkload(workloadId)) .Distinct() .Select(packId => _workloadResolver.TryGetPackInfo(packId)) .Where(pack => pack != null); var installedPacks = installer.GetInstalledPacks(currentFeatureBand); var packsToUpdate = new List <PackInfo>(); foreach (var updatedPack in updatedPacks) { var installedPackIds = installedPacks.Select(pack => pack.Id); if (installedPackIds.Contains(updatedPack.Id)) { var installedPack = installedPacks.First(pack => pack.Id.Equals(updatedPack.Id)); var installedVersion = new ReleaseVersion(installedPack.Version); var updatedVersion = new ReleaseVersion(updatedPack.Version); if (installedVersion != null && updatedVersion != null && installedVersion < updatedVersion) { packsToUpdate.Add(updatedPack); } } else { // New pack required for this workload, include it packsToUpdate.Add(updatedPack); } } return(packsToUpdate); }
private IEnumerable <PackInfo> GetPacksToInstall(IEnumerable <WorkloadId> workloadIds) { var installedPacks = _workloadInstaller.GetPackInstaller().GetInstalledPacks(_sdkFeatureBand); return(workloadIds .SelectMany(workloadId => _workloadResolver.GetPacksInWorkload(workloadId)) .Distinct() .Select(packId => _workloadResolver.TryGetPackInfo(packId)) .Where(pack => pack != null) .Where(pack => !installedPacks.Contains((pack.Id, pack.Version)))); }
private void InstallWorkloadsWithInstallRecord(IEnumerable <WorkloadId> workloadIds, SdkFeatureBand sdkFeatureBand) { if (_workloadInstaller.GetInstallationUnit().Equals(InstallationUnit.Packs)) { var installer = _workloadInstaller.GetPackInstaller(); var workloadPackToInstall = workloadIds .SelectMany(workloadId => _workloadResolver.GetPacksInWorkload(workloadId.ToString())) .Distinct() .Select(packId => _workloadResolver.TryGetPackInfo(packId)); TransactionalAction.Run( action: () => { foreach (var packId in workloadPackToInstall) { installer.InstallWorkloadPack(packId, sdkFeatureBand); } foreach (var workloadId in workloadIds) { _workloadInstaller.WriteWorkloadInstallationRecord(workloadId, sdkFeatureBand); } }, rollback: () => { foreach (var packId in workloadPackToInstall) { installer.RollBackWorkloadPackInstall(packId, sdkFeatureBand); } foreach (var workloadId in workloadIds) { _workloadInstaller.DeleteWorkloadInstallationRecord(workloadId, sdkFeatureBand); } }); } else { var installer = _workloadInstaller.GetWorkloadInstaller(); foreach (var workloadId in workloadIds) { installer.InstallWorkload(workloadId); } } }
private void ReinstallWorkloadsBasedOnCurrentManifests(IEnumerable <WorkloadId> workloadIds, SdkFeatureBand sdkFeatureBand) { if (_workloadInstaller.GetInstallationUnit().Equals(InstallationUnit.Packs)) { var installer = _workloadInstaller.GetPackInstaller(); var packsToInstall = workloadIds .SelectMany(workloadId => _workloadResolver.GetPacksInWorkload(workloadId)) .Distinct() .Select(packId => _workloadResolver.TryGetPackInfo(packId)) .Where(pack => pack != null); foreach (var packId in packsToInstall) { CliTransaction.RunNew(context => installer.RepairWorkloadPack(packId, sdkFeatureBand, context)); } } else { throw new NotImplementedException(); } }
/// <summary> /// Cleans up and removes stale workload packs. /// </summary> public void GarbageCollectInstalledWorkloadPacks() { try { Log?.LogMessage("Starting garbage collection."); IEnumerable <SdkFeatureBand> installedFeatureBands = GetInstalledFeatureBands(); IEnumerable <WorkloadId> installedWorkloads = RecordRepository.GetInstalledWorkloads(_sdkFeatureBand); IEnumerable <PackInfo> expectedWorkloadPacks = installedWorkloads .SelectMany(workload => _workloadResolver.GetPacksInWorkload(workload)) .Select(pack => _workloadResolver.TryGetPackInfo(pack)) .Where(pack => pack != null); IEnumerable <WorkloadPackId> expectedPackIds = expectedWorkloadPacks.Select(p => p.Id); foreach (PackInfo expectedPack in expectedWorkloadPacks) { Log?.LogMessage($"Expected workload pack, ID: {expectedPack.ResolvedPackageId}, version: {expectedPack.Version}."); } foreach (SdkFeatureBand installedFeatureBand in installedFeatureBands) { Log?.LogMessage($"Installed feature band: {installedFeatureBand}"); } IEnumerable <WorkloadPackRecord> installedWorkloadPacks = WorkloadPackRecords.Values.SelectMany(r => r); List <WorkloadPackRecord> packsToRemove = new List <WorkloadPackRecord>(); // We first need to clean up the dependents and then do a pass at removing them. Querying the installed packs // is effectively a table scan of the registry to make sure we have accurate information and there's a // potential perf hit for both memory and speed when enumerating large sets of registry entries. foreach (WorkloadPackRecord packRecord in installedWorkloadPacks) { DependencyProvider depProvider = new DependencyProvider(packRecord.ProviderKeyName); // Find all the dependents that look like they belong to SDKs. We only care // about dependents that match the SDK host we're running under. For example, an x86 SDK should not be // modifying the x64 MSI dependents. IEnumerable <string> sdkDependents = depProvider.Dependents .Where(d => d.StartsWith($"{DependentPrefix}")) .Where(d => d.EndsWith($",{HostArchitecture}")); foreach (string dependent in sdkDependents) { Log?.LogMessage($"Evaluating dependent for workload pack, dependent: {dependent}, pack ID: {packRecord.PackId}, pack version: {packRecord.PackVersion}"); // Dependents created by the SDK should have 3 parts, for example, "Microsoft.NET.Sdk,6.0.100,x86". string[] dependentParts = dependent.Split(','); if (dependentParts.Length != 3) { Log?.LogMessage($"Skipping dependent: {dependent}"); continue; } try { SdkFeatureBand dependentFeatureBand = new SdkFeatureBand(dependentParts[1]); if (!installedFeatureBands.Contains(dependentFeatureBand)) { Log?.LogMessage($"Removing dependent '{dependent}' from provider key '{depProvider.ProviderKeyName}' because its SDK feature band does not match any installed feature bands."); UpdateDependent(InstallRequestType.RemoveDependent, depProvider.ProviderKeyName, dependent); } if (dependentFeatureBand.Equals(_sdkFeatureBand)) { // If the current SDK feature band is listed as a dependent, we can validate // the workload pack against the expected pack IDs and versions to potentially remove it. if (!expectedWorkloadPacks.Where(p => packRecord.PackId.Equals(p.ResolvedPackageId)) .Where(p => p.Version.Equals(packRecord.PackVersion.ToString())).Any()) { Log?.LogMessage($"Removing dependent '{dependent}' because the pack record does not match any expected packs."); UpdateDependent(InstallRequestType.RemoveDependent, depProvider.ProviderKeyName, dependent); } } } catch (Exception e) { Log?.LogMessage($"{e.Message}"); Log?.LogMessage($"{e.StackTrace}"); continue; } } // Recheck the registry to see if there are any remaining dependents. If not, we can // remove the workload pack. We'll add it to the list and remove the packs at the end. IEnumerable <string> remainingDependents = depProvider.Dependents; if (remainingDependents.Any()) { Log?.LogMessage($"{packRecord.PackId} ({packRecord.PackVersion}) will not be removed because other dependents remain: {string.Join(", ", remainingDependents)}."); } else { packsToRemove.Add(packRecord); } } foreach (WorkloadPackRecord record in packsToRemove) { // We need to make sure the product is actually installed and that we're not dealing with an orphaned record, e.g. // if a previous removal was interrupted. We can't safely clean up orphaned records because it's too expensive // to query all installed components and determine the product codes associated with the component that // created the record. DetectState state = Detect(record.ProductCode, out string _); if (state == DetectState.Present) { // We don't have package information and can't construct it accurately. string id = $"{record.PackId}.Msi.{HostArchitecture}"; MsiPayload msiPayload = GetCachedMsiPayload(id, record.PackVersion.ToString()); VerifyPackage(msiPayload); InstallAction plannedAction = GetPlannedAction(state, InstallAction.Uninstall); string logFile = GetMsiLogName(record, plannedAction); uint error = ExecuteWithProgress($"Removing {id} ", () => UninstallMsi(record.ProductCode, logFile)); ExitOnError(error, $"Failed to uninstall {msiPayload.MsiPath}."); } } } catch (Exception e) { LogException(e); throw; } }
public override SdkResult Resolve(SdkReference sdkReference, SdkResolverContext resolverContext, SdkResultFactory factory) { if (!_enabled) { return(null); } InitializeWorkloadResolver(resolverContext); if (sdkReference.Name.Equals("Microsoft.NET.SDK.WorkloadAutoImportPropsLocator", StringComparison.OrdinalIgnoreCase)) { List <string> autoImportSdkPaths = new List <string>(); foreach (var sdkPackInfo in _workloadResolver.GetInstalledWorkloadPacksOfKind(WorkloadPackKind.Sdk)) { string sdkPackSdkFolder = Path.Combine(sdkPackInfo.Path, "Sdk"); string autoImportPath = Path.Combine(sdkPackSdkFolder, "AutoImport.props"); if (File.Exists(autoImportPath)) { autoImportSdkPaths.Add(sdkPackSdkFolder); } } // Call Distinct() here because with aliased packs, there may be duplicates of the same path return(factory.IndicateSuccess(autoImportSdkPaths.Distinct(), sdkReference.Version)); } else if (sdkReference.Name.Equals("Microsoft.NET.SDK.WorkloadManifestTargetsLocator", StringComparison.OrdinalIgnoreCase)) { List <string> workloadManifestPaths = new List <string>(); foreach (var manifestDirectory in _workloadManifestProvider.GetManifestDirectories()) { var workloadManifestTargetPath = Path.Combine(manifestDirectory, "WorkloadManifest.targets"); if (File.Exists(workloadManifestTargetPath)) { workloadManifestPaths.Add(manifestDirectory); } } return(factory.IndicateSuccess(workloadManifestPaths, sdkReference.Version)); } else { var packInfo = _workloadResolver.TryGetPackInfo(sdkReference.Name); if (packInfo != null) { if (Directory.Exists(packInfo.Path)) { return(factory.IndicateSuccess(Path.Combine(packInfo.Path, "Sdk"), sdkReference.Version)); } else { var itemsToAdd = new Dictionary <string, SdkResultItem>(); itemsToAdd.Add("MissingWorkloadPack", new SdkResultItem(sdkReference.Name, metadata: new Dictionary <string, string>() { { "Version", packInfo.Version } })); Dictionary <string, string> propertiesToAdd = new Dictionary <string, string>(); return(factory.IndicateSuccess(Enumerable.Empty <string>(), sdkReference.Version, propertiesToAdd: propertiesToAdd, itemsToAdd: itemsToAdd)); } } } return(null); }
/// <summary> /// Cleans up and removes stale workload packs. /// </summary> public void GarbageCollectInstalledWorkloadPacks(DirectoryPath?offlineCache = null) { try { ReportPendingReboot(); Log?.LogMessage("Starting garbage collection."); IEnumerable <SdkFeatureBand> installedFeatureBands = GetInstalledFeatureBands(); IEnumerable <WorkloadId> installedWorkloads = RecordRepository.GetInstalledWorkloads(_sdkFeatureBand); Dictionary <(WorkloadPackId id, string version), PackInfo> expectedWorkloadPacks = installedWorkloads .SelectMany(workload => _workloadResolver.GetPacksInWorkload(workload)) .Distinct() .Select(pack => _workloadResolver.TryGetPackInfo(pack)) .Where(pack => pack != null) .ToDictionary(p => (new WorkloadPackId(p.ResolvedPackageId), p.Version)); foreach (PackInfo expectedPack in expectedWorkloadPacks.Values) { Log?.LogMessage($"Expected workload pack, ID: {expectedPack.ResolvedPackageId}, version: {expectedPack.Version}."); } foreach (SdkFeatureBand installedFeatureBand in installedFeatureBands) { Log?.LogMessage($"Installed feature band: {installedFeatureBand}"); } IEnumerable <WorkloadPackRecord> installedWorkloadPacks = GetWorkloadPackRecords(); List <WorkloadPackRecord> packsToRemove = new List <WorkloadPackRecord>(); // We first need to clean up the dependents and then do a pass at removing them. Querying the installed packs // is effectively a table scan of the registry to make sure we have accurate information and there's a // potential perf hit for both memory and speed when enumerating large sets of registry entries. foreach (WorkloadPackRecord packRecord in installedWorkloadPacks) { DependencyProvider depProvider = new DependencyProvider(packRecord.ProviderKeyName); // Find all the dependents that look like they belong to SDKs. We only care // about dependents that match the SDK host we're running under. For example, an x86 SDK should not be // modifying the x64 MSI dependents. IEnumerable <string> sdkDependents = depProvider.Dependents .Where(d => d.StartsWith($"{DependentPrefix}")) .Where(d => d.EndsWith($",{HostArchitecture}")); foreach (string dependent in sdkDependents) { Log?.LogMessage($"Evaluating dependent for workload pack, dependent: {dependent}, MSI ID: {packRecord.MsiId}, MSI version: {packRecord.MsiNuGetVersion}"); // Dependents created by the SDK should have 3 parts, for example, "Microsoft.NET.Sdk,6.0.100,x86". string[] dependentParts = dependent.Split(','); if (dependentParts.Length != 3) { Log?.LogMessage($"Skipping dependent: {dependent}"); continue; } try { SdkFeatureBand dependentFeatureBand = new SdkFeatureBand(dependentParts[1]); if (!installedFeatureBands.Contains(dependentFeatureBand)) { Log?.LogMessage($"Removing dependent '{dependent}' from provider key '{depProvider.ProviderKeyName}' because its SDK feature band does not match any installed feature bands."); UpdateDependent(InstallRequestType.RemoveDependent, depProvider.ProviderKeyName, dependent); } if (dependentFeatureBand.Equals(_sdkFeatureBand)) { // If the current SDK feature band is listed as a dependent, we can validate // the workload packs against the expected pack IDs and versions to potentially remove it. if (packRecord.InstalledPacks.All(p => !expectedWorkloadPacks.ContainsKey((p.id, p.version.ToString())))) { // None of the packs installed by this MSI are necessary any longer for this feature band, so we can remove the reference count Log?.LogMessage($"Removing dependent '{dependent}' because the pack record(s) do not match any expected packs."); UpdateDependent(InstallRequestType.RemoveDependent, depProvider.ProviderKeyName, dependent); } } } catch (Exception e) { Log?.LogMessage($"{e.Message}"); Log?.LogMessage($"{e.StackTrace}"); continue; } } // Recheck the registry to see if there are any remaining dependents. If not, we can // remove the workload pack. We'll add it to the list and remove the packs at the end. IEnumerable <string> remainingDependents = depProvider.Dependents; if (remainingDependents.Any()) { Log?.LogMessage($"{packRecord.MsiId} ({packRecord.MsiNuGetVersion}) will not be removed because other dependents remain: {string.Join(", ", remainingDependents)}."); } else { packsToRemove.Add(packRecord); } } foreach (WorkloadPackRecord record in packsToRemove) { // We need to make sure the product is actually installed and that we're not dealing with an orphaned record, e.g. // if a previous removal was interrupted. We can't safely clean up orphaned records because it's too expensive // to query all installed components and determine the product codes associated with the component that // created the record. DetectState state = DetectPackage(record.ProductCode, out Version _); if (state == DetectState.Present) { // Manually construct the MSI payload package details string id = $"{record.MsiId}.Msi.{HostArchitecture}"; MsiPayload msi = GetCachedMsiPayload(id, record.MsiNuGetVersion.ToString(), offlineCache); // Make sure the package we have in the cache matches with the record. If it doesn't, we'll do the uninstall // the hard way if (!string.Equals(record.ProductCode, msi.ProductCode, StringComparison.OrdinalIgnoreCase)) { Log?.LogMessage($"ProductCode mismatch! Cached package: {msi.ProductCode}, pack record: {record.ProductCode}."); string logFile = GetMsiLogName(record, InstallAction.Uninstall); uint error = ExecuteWithProgress(String.Format(LocalizableStrings.MsiProgressUninstall, id), () => UninstallMsi(record.ProductCode, logFile)); ExitOnError(error, $"Failed to uninstall {msi.MsiPath}."); } else { // No need to plan. We know that there are no other dependents, the MSI is installed and we // want to remove it. VerifyPackage(msi); ExecutePackage(msi, InstallAction.Uninstall); } } } } catch (Exception e) { LogException(e); throw; } }