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)));
        }
Beispiel #2
0
        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));
        }
Beispiel #3
0
        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)));
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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))));
        }
Beispiel #6
0
        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);
                }
            }
        }
Beispiel #7
0
        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();
            }
        }
Beispiel #8
0
        /// <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;
            }
        }
Beispiel #9
0
        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);
        }
Beispiel #10
0
        /// <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;
            }
        }