应用服务管理模块,从Stardust迁移到StarAgent
智能大石头 authored at 2024-07-08 20:51:34
7.27 KiB
Stardust
using NewLife;
using NewLife.Log;
using NewLife.Remoting.Clients;
using NewLife.Serialization;
using Stardust.Models;
#if NET45_OR_GREATER || NETCOREAPP || NETSTANDARD
using TaskEx = System.Threading.Tasks.Task;
#endif

namespace Stardust.Managers;

/// <summary>框架管理器</summary>
public class FrameworkManager
{
    private IEventProvider? _eventProvider;

    /// <summary>获取所有框架版本</summary>
    /// <returns></returns>
    public String[] GetAllVersions()
    {
        var vers = new List<VerInfo>();
#if NET5_0_OR_GREATER
        if (OperatingSystem.IsWindows())
        {
            vers.AddRange(NetRuntime.Get1To45VersionFromRegistry());
            vers.AddRange(NetRuntime.Get45PlusFromRegistry());
        }
#else
        vers.AddRange(NetRuntime.Get1To45VersionFromRegistry());
        vers.AddRange(NetRuntime.Get45PlusFromRegistry());
#endif
        vers.AddRange(NetRuntime.GetNetCore(false));

        //return vers.Join(",", e => e.Name.TrimStart('v'));
        return vers.Select(e => e.Name).ToArray();
    }

    /// <summary>附加刷新命令</summary>
    /// <param name="client"></param>
    public void Attach(ICommandClient client)
    {
        _eventProvider = client as IEventProvider;

        client.RegisterCommand("framework/install", DoInstall);
        client.RegisterCommand("framework/uninstall", DoUninstall);
    }

    private String? DoInstall(String? argument)
    {
        var model = argument?.ToJsonEntity<FrameworkModel>();
        if (model == null || model.Version.IsNullOrEmpty()) throw new Exception("未指定版本!");

        var nr = new NetRuntime
        {
            Silent = true,
            Force = model.Force,
            EventProvider = _eventProvider,
            Log = XTrace.Log,
        };
        if (!model.BaseUrl.IsNullOrEmpty()) nr.BaseUrl = model.BaseUrl;

        // 获取已安装版本集合
        var ver = model.Version.Trim('v', 'V');
        if (Runtime.Linux)
        {
            var kind = "";
            var p = ver.IndexOf('-');
            if (p > 0)
            {
                kind = ver.Substring(p + 1);
                ver = ver.Substring(0, p);
            }

            nr.InstallNetOnLinux(ver, kind);
        }
        else if (ver.StartsWithIgnoreCase("4.0"))
        {
#if NET5_0_OR_GREATER
            if (OperatingSystem.IsWindows())
                nr.InstallNet40();
#else
            nr.InstallNet40();
#endif
        }
        else if (ver.StartsWithIgnoreCase("4.5"))
        {
#if NET5_0_OR_GREATER
            if (OperatingSystem.IsWindows())
                nr.InstallNet45();
#else
            nr.InstallNet45();
#endif
        }
        else if (ver.StartsWithIgnoreCase("4."))
        {
#if NET5_0_OR_GREATER
            if (OperatingSystem.IsWindows())
                nr.InstallNet48();
#else
            nr.InstallNet48();
#endif
        }
        else if (ver.StartsWithIgnoreCase("6."))
        {
            var kind = "";
            var p = ver.IndexOf('-');
            if (p > 0)
            {
                kind = ver.Substring(p + 1);
                ver = ver.Substring(0, p);
            }

            nr.InstallNet6(ver, kind);
        }
        else if (ver.StartsWithIgnoreCase("7."))
        {
            var kind = "";
            var p = ver.IndexOf('-');
            if (p > 0)
            {
                kind = ver.Substring(p + 1);
                ver = ver.Substring(0, p);
            }

            nr.InstallNet7(ver, kind);
        }
        else if (ver.StartsWithIgnoreCase("8."))
        {
            var kind = "";
            var p = ver.IndexOf('-');
            if (p > 0)
            {
                kind = ver.Substring(p + 1);
                ver = ver.Substring(0, p);
            }

            nr.InstallNet8(ver, kind);
        }
        else
            throw new Exception($"不支持的.NET版本[{ver}]");

        CheckPing();

        return "安装成功";
    }

    private String? DoUninstall(String? argument)
    {
        var model = argument?.ToJsonEntity<FrameworkModel>();
        if (model == null || model.Version.IsNullOrEmpty()) throw new Exception("未指定版本!");

        //目前仅支持NetCore
        var ver = model.Version.Trim('v', 'V');
        var deleted = false;
        var versions = NetRuntime.GetNetCore(false);

        foreach (var version in versions)
        {
            //删除指定
            var currentVer = version.Name.TrimStart('v', 'V');
            if (ver != currentVer) { continue; }

            if (Runtime.Linux)
            {
                var runtimes = new String[] { "Microsoft.NETCore.App", "Microsoft.AspNetCore.App" };
                var rootDir = "/usr/share/dotnet/shared";
                foreach (var runtime in runtimes)
                {
                    var dir = rootDir.CombinePath(runtime, currentVer);
                    if (Directory.Exists(dir))
                    {
                        //有可能被占用
                        try
                        {
                            Directory.Delete(dir, true);
                            deleted = true;
                            XTrace.Log.Info($"{runtime} {currentVer} 已删除");
                        }
                        catch (Exception ex)
                        {
                            XTrace.Log.Info($"卸载时出现异常 {ex.Message}");
                        }
                    }
                }
            }
            else if (Runtime.Windows)
            {
                var runtimes = new String[] { "Microsoft.NETCore.App", "Microsoft.AspNetCore.App", "Microsoft.WindowsDesktop.App" };
                var rootDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles).CombinePath("dotnet", "shared");
                foreach (var runtime in runtimes)
                {
                    var dir = rootDir.CombinePath(runtime, currentVer);
                    if (Directory.Exists(dir))
                    {
                        try
                        {
                            Directory.Delete(dir, true);
                            deleted = true;
                            XTrace.Log.Info($"{runtime} {currentVer} 已删除");
                        }
                        catch (Exception ex)
                        {
                            XTrace.Log.Info($"卸载时出现异常 {ex.Message}");
                        }                        
                    }
                }
            }
            else
            {
                XTrace.Log.Info("暂不支持当前OS卸载");
                throw new Exception("暂不支持当前OS卸载");
            }
        }

        XTrace.Log.Info("{0} 卸载成功", model.Version);
        CheckPing();

        return deleted ? "卸载成功" : "卸载失败";
    }

    /// <summary>星尘安装卸载框架后,马上执行一次心跳,使得其尽快上报框架版本</summary>
    void CheckPing()
    {
        if (_eventProvider is StarClient client)
        {
            TaskEx.Run(async () =>
            {
                await client.Ping();
                await TaskEx.Delay(1000);

                //!! 要执行整个升级动作,而不仅仅是拉取新版本
                //await client.Upgrade("", "");
                await client.SendCommand("node/upgrade", "");
            });
        }
    }
}