< Summary

Information
Class: Dotnet.Installer.Core.Models.Component
Assembly: Dotnet.Installer.Core
File(s): /home/runner/work/dotnet-snap/dotnet-snap/src/Dotnet.Installer.Core/Models/Component.cs
Line coverage
61%
Covered lines: 96
Uncovered lines: 60
Coverable lines: 156
Total lines: 260
Line coverage: 61.5%
Branch coverage
40%
Covered branches: 36
Total branches: 88
Branch coverage: 40.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Key()100%11100%
get_Name()100%11100%
get_Description()100%11100%
get_MajorVersion()100%11100%
get_IsLts()100%11100%
get_Grade()100%11100%
get_EndOfLife()100%11100%
get_Dependencies()100%11100%
get_Installation()100%11100%
get_IsInstalled()100%11100%
Install()63.63%262279.31%
Uninstall()75%4476.92%
PlaceMountUnits()33.33%9658.82%
RemoveMountUnits()33.33%9657.14%
Mount()8.33%761223.52%
Unmount()14.28%771431.57%
PlacePathUnits()50%171266.66%
RemovePathUnits()50%171266.66%

File(s)

/home/runner/work/dotnet-snap/dotnet-snap/src/Dotnet.Installer.Core/Models/Component.cs

#LineLine coverage
 1using System.Text;
 2using System.Text.Json.Serialization;
 3using Dotnet.Installer.Core.Models.Events;
 4using Dotnet.Installer.Core.Services.Contracts;
 5
 6namespace Dotnet.Installer.Core.Models;
 7
 8public enum Grade
 9{
 10    Rtm,
 11    Rc,
 12    Preview
 13}
 14
 15public class Component
 16{
 8117    public required string Key { get; init; }
 918    public required string Name { get; init; }
 919    public required string Description { get; init; }
 920    public required int MajorVersion { get; init; }
 921    public required bool IsLts { get; init; }
 22    // NOTE: This property needs a default value to ensure backward compatibility with existing manifests
 23    // that might not have this field set. New manifests should always include this field.
 24    // 'Grade' was introduced when preview versions of .NET started being made available, therefore it is safe
 25    // to assume that entries without this field are RTM.
 26    [JsonConverter(typeof(JsonStringEnumConverter<Grade>))]
 1827    public Grade Grade { get; init; } = Grade.Rtm;
 928    [JsonPropertyName("eol")] public DateTime? EndOfLife { get; init; }
 3229    public required IEnumerable<string> Dependencies { get; init; }
 930    public Installation? Installation { get; set; }
 731    public bool IsInstalled => Installation is not null;
 32
 33    public event EventHandler<InstallationStartedEventArgs>? InstallationStarted;
 34    public event EventHandler<InstallationFinishedEventArgs>? InstallationFinished;
 35
 36    public async Task Install(IFileService fileService, IManifestService manifestService, ISnapService snapService,
 37        ISystemdService systemdService, ILogger? logger = null)
 538    {
 539        if (IsInstalled)
 040        {
 041            logger?.LogInformation($"{Description} already installed!");
 042            return;
 43        }
 44
 545        InstallationStarted?.Invoke(this, new InstallationStartedEventArgs(Key));
 46
 47        // Install content snap on the machine
 548        if (!snapService.IsSnapInstalled(Key))
 549        {
 50            // Gather highest channel available
 551            var snapInfo = await snapService.FindSnap(Key);
 52
 553            var channel = snapInfo?.Channel switch
 554            {
 055                "candidate" => SnapChannel.Candidate,
 056                "beta" => SnapChannel.Beta,
 057                "edge" => SnapChannel.Edge,
 558                _ => SnapChannel.Stable
 559            };
 60
 561            var result = await snapService.Install(Key, channel);
 562            if (!result.IsSuccess) throw new ApplicationException(result.StandardError);
 563        }
 64
 65        // Place linking file in the content snap's $SNAP_COMMON
 566        await fileService.PlaceLinkageFile(Key);
 67
 68        // Install Systemd mount units
 569        await PlaceMountUnits(fileService, manifestService, systemdService, logger);
 70
 71        // Install update watcher unit
 572        await PlacePathUnits(fileService, systemdService, logger);
 73
 74        // Register the installation of this component in the local manifest file
 575        await manifestService.Add(this);
 76
 1977        foreach (var dependency in Dependencies)
 278        {
 779            var component = manifestService.Remote.First(c => c.Key == dependency);
 280            await component.Install(fileService, manifestService, snapService, systemdService, logger);
 281        }
 82
 583        InstallationFinished?.Invoke(this, new InstallationFinishedEventArgs(Key));
 584    }
 85
 86    public async Task Uninstall(IFileService fileService, IManifestService manifestService, ISnapService snapService,
 87        ISystemdService systemdService, ILogger? logger = default)
 188    {
 189        if (IsInstalled)
 190        {
 91            // Uninstall systemd mount units
 192            await RemoveMountUnits(fileService, manifestService, systemdService, logger);
 93
 94            // Uninstall systemd path units
 195            await RemovePathUnits(fileService, systemdService, logger);
 96
 197            if (snapService.IsSnapInstalled(Key))
 098            {
 099                await snapService.Remove(Key, purge: true);
 0100            }
 101
 1102            Installation = null;
 1103            await manifestService.Remove(this);
 1104        }
 1105    }
 106
 107    public async Task PlaceMountUnits(IFileService fileService, IManifestService manifestService,
 108        ISystemdService systemdService, ILogger? logger = default)
 5109    {
 5110        var units = new StringBuilder();
 5111        var unitPaths = fileService.EnumerateContentSnapMountFiles(Key);
 112
 15113        foreach (var unitPath in unitPaths)
 0114        {
 0115            logger?.LogDebug($"Copying {unitPath} to systemd directory.");
 0116            fileService.InstallSystemdMountUnit(unitPath);
 0117            units.AppendLine(unitPath.Split('/').Last());
 0118        }
 119
 120        // Save unit names to component .mounts file
 5121        await fileService.PlaceUnitsFile(manifestService.SnapConfigurationLocation, contentSnapName: Key,
 5122            units.ToString());
 123
 5124        var result = await systemdService.DaemonReload();
 5125        if (!result.IsSuccess)
 0126        {
 0127            throw new ApplicationException("Could not reload systemd daemon");
 128        }
 5129        await Mount(manifestService, fileService, systemdService, logger);
 5130    }
 131
 132    public async Task RemoveMountUnits(IFileService fileService, IManifestService manifestService,
 133        ISystemdService systemdService, ILogger? logger = default)
 1134    {
 1135        await Unmount(fileService, manifestService, systemdService, logger);
 136
 1137        var units = await fileService.ReadUnitsFile(manifestService.SnapConfigurationLocation, Key);
 138
 3139        foreach (var unit in units)
 0140        {
 0141            logger?.LogDebug($"Removing {unit} from systemd directory.");
 0142            fileService.UninstallSystemdMountUnit(unit);
 0143        }
 144
 1145        fileService.DeleteUnitsFile(manifestService.SnapConfigurationLocation, Key);
 146
 1147        var result = await systemdService.DaemonReload();
 1148        if (!result.IsSuccess)
 0149        {
 0150            throw new ApplicationException("Could not reload systemd daemon");
 151        }
 1152    }
 153
 154    public async Task Mount(IManifestService manifestService, IFileService fileService, ISystemdService systemdService,
 155        ILogger? logger = default)
 5156    {
 5157        var units = await fileService.ReadUnitsFile(manifestService.SnapConfigurationLocation, Key);
 158
 15159        foreach (var unit in units)
 0160        {
 0161            var result = await systemdService.EnableUnit(unit);
 0162            if (!result.IsSuccess)
 0163            {
 0164                throw new ApplicationException($"Could not enable unit {unit}");
 165            }
 0166            logger?.LogDebug($"Enabled {unit}");
 167
 0168            result = await systemdService.StartUnit(unit);
 0169            if (!result.IsSuccess)
 0170            {
 0171                throw new ApplicationException($"Could not start unit {unit}");
 172            }
 0173            logger?.LogDebug($"Started {unit}");
 174
 0175            logger?.LogDebug($"Finished mounting {unit}");
 0176        }
 5177    }
 178
 179    public async Task Unmount(IFileService fileService, IManifestService manifestService,
 180        ISystemdService systemdService, ILogger? logger = default)
 1181    {
 1182        var units = await fileService.ReadUnitsFile(manifestService.SnapConfigurationLocation, Key);
 183
 3184        foreach (var unit in units)
 0185        {
 0186            var result = await systemdService.DisableUnit(unit);
 0187            if (!result.IsSuccess)
 0188            {
 0189                throw new ApplicationException($"Could not disable unit {unit}");
 190            }
 0191            logger?.LogDebug($"Disabled {unit}");
 192
 0193            result = await systemdService.StopUnit(unit);
 0194            if (!result.IsSuccess)
 0195            {
 0196                throw new ApplicationException($"Could not stop unit {unit}");
 197            }
 0198            logger?.LogDebug($"Stopped {unit}");
 199
 0200            logger?.LogDebug($"Finished unmounting {unit}");
 0201        }
 202
 203        // Check for any empty directories
 1204        fileService.RemoveEmptyDirectories(manifestService.DotnetInstallLocation);
 1205        logger?.LogDebug("Removed empty directories.");
 1206    }
 207
 208    private async Task PlacePathUnits(IFileService fileService, ISystemdService systemdService, ILogger? logger = null)
 5209    {
 5210        fileService.InstallSystemdPathUnit(Key);
 5211        logger?.LogDebug($"Placed upgrade watcher path and service units for snap {Key}");
 212
 5213        var result = await systemdService.DaemonReload();
 5214        if (!result.IsSuccess)
 0215        {
 0216            throw new ApplicationException("Could not reload systemd daemon");
 217        }
 218
 5219        result = await systemdService.EnableUnit($"{Key}-update-watcher.path");
 5220        if (!result.IsSuccess)
 0221        {
 0222            throw new ApplicationException($"Could not enable {Key}-update-watcher.path");
 223        }
 5224        logger?.LogDebug($"Enabled {Key}-update-watcher.path");
 225
 5226        result = await systemdService.StartUnit($"{Key}-update-watcher.path");
 5227        if (!result.IsSuccess)
 0228        {
 0229            throw new ApplicationException($"Could not start {Key}-update-watcher.path");
 230        }
 5231        logger?.LogDebug($"Started {Key}-update-watcher.path");
 5232    }
 233
 234    private async Task RemovePathUnits(IFileService fileService, ISystemdService systemdService,
 235        ILogger? logger = default)
 1236    {
 1237        var result = await systemdService.DisableUnit($"{Key}-update-watcher.path");
 1238        if (!result.IsSuccess)
 0239        {
 0240            throw new ApplicationException($"Could not disable {Key}-update-watcher.path");
 241        }
 1242        logger?.LogDebug($"Disabled {Key}-update-watcher.path");
 243
 1244        result = await systemdService.StopUnit($"{Key}-update-watcher.path");
 1245        if (!result.IsSuccess)
 0246        {
 0247            throw new ApplicationException($"Could not stop {Key}-update-watcher.path");
 248        }
 1249        logger?.LogDebug($"Stopped {Key}-update-watcher.path");
 250
 1251        fileService.UninstallSystemdPathUnit(Key);
 1252        logger?.LogDebug($"Removed upgrade watcher path and service units for snap {Key}");
 253
 1254        result = await systemdService.DaemonReload();
 1255        if (!result.IsSuccess)
 0256        {
 0257            throw new ApplicationException("Could not reload systemd daemon");
 258        }
 1259    }
 260}