Skip to content

Commit

Permalink
重构节点版本的策略匹配,在埋点数据标签中记录策略没有匹配上的原因,彻底解决经常搞不明白节点为何没有匹配策略的问题。策略中字符串区分大小写,…
Browse files Browse the repository at this point in the history
…例如*a2*就不能匹配A2;

自动检测并推送dotNet运行时的功能全面测试通过。
  • Loading branch information
nnhy committed Dec 29, 2023
1 parent f76837e commit f6980a7
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 62 deletions.
2 changes: 1 addition & 1 deletion StarAgent/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ private async Task TryConnectServer(Object state)
_timer.TryDispose();
_timer = new TimerX(CheckUpgrade, null, 5_000, 600_000) { Async = true };

client.RegisterCommand("node/upgrade", s => _timer.SetNext(-1));
client.RegisterCommand("node/upgrade", s => _ = CheckUpgrade(s));
client.RegisterCommand("node/restart", Restart);
client.RegisterCommand("node/reboot", Reboot);
}
Expand Down
9 changes: 6 additions & 3 deletions Stardust.Data/Nodes/节点.Biz.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,12 @@ public override void Valid(Boolean isNew)

if (Period == 0) Period = 60;

// 自动识别版本
var kind = OSKindHelper.Parse(OS, OSVersion);
if (kind > 0) OSKind = kind;
if (!Dirtys[__.OSKind])
{
// 自动识别版本
var kind = OSKindHelper.Parse(OS, OSVersion);
if (kind > 0) OSKind = kind;
}
}

/// <summary>已重载</summary>
Expand Down
76 changes: 41 additions & 35 deletions Stardust.Data/Nodes/节点版本.Biz.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Xml.Serialization;
using NewLife;
using NewLife.Data;
using NewLife.Log;
using Stardust.Models;
using XCode;
using XCode.Membership;
Expand Down Expand Up @@ -156,66 +157,69 @@ public static IList<NodeVersion> GetValids(NodeChannels channel)
/// <returns></returns>
public Boolean Match(Node node)
{
return Match(node, node.Product);
var rs = MatchResult(node);
if (rs == null) return true;

DefaultSpan.Current?.AppendTag($"[{ID}][{Version}] {rs}");

return false;
}

/// <summary>应用策略是否匹配指定节点</summary>
/// <param name="node"></param>
/// <param name="productCode">目标产品编码。可以是StarAgent/CrazyCoder/dotNet等</param>
/// <returns></returns>
public Boolean Match(Node node, String productCode)
public String MatchResult(Node node)
{
// 比较产品类型
if (!ProductCode.IsNullOrEmpty() && !ProductCode.EqualIgnoreCase(productCode)) return false;

// 没有使用该规则,直接过
if (Rules.TryGetValue("version", out var vs))
{
var ver = node.Version;
if (ver.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(ver))) return false;
if (ver.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(ver))) return $"[{ver}] not Match {vs.Join(",")}";
}
else if (Rules.TryGetValue("version>", out vs))
{
if (node.Version.IsNullOrEmpty()) return false;
if (!System.Version.TryParse(node.Version, out var ver1)) return false;
if (!System.Version.TryParse(vs[0], out var ver2)) return false;
var ver = node.Version;
if (node.Version.IsNullOrEmpty()) return "Version is null";
if (!System.Version.TryParse(ver, out var ver1)) return $"Version=[{ver}] is invalid";
if (!System.Version.TryParse(vs[0], out var ver2)) return $"vs[0]=[{vs[0]}] is invalid";

if (ver1 < ver2) return false;
if (ver1 < ver2) return $"Version=[{ver1}] < {ver2}";
}
else if (Rules.TryGetValue("version<", out vs))
{
if (node.Version.IsNullOrEmpty()) return false;
if (!System.Version.TryParse(node.Version, out var ver1)) return false;
if (!System.Version.TryParse(vs[0], out var ver2)) return false;
var ver = node.Version;
if (node.Version.IsNullOrEmpty()) return "Version is null";
if (!System.Version.TryParse(ver, out var ver1)) return $"Version=[{ver}] is invalid";
if (!System.Version.TryParse(vs[0], out var ver2)) return $"vs[0]=[{vs[0]}] is invalid";

if (ver1 > ver2) return false;
if (ver1 > ver2) return $"Version=[{ver1}] > {ver2}";
}

if (Rules.TryGetValue("node", out vs))
{
var code = node.Code;
var name = node.Name;
if ((code.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(code))) &&
(name.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(name)))) return false;
(name.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(name)))) return $"[{code}/{name}] not Match {vs.Join(",")}";
}

if (Rules.TryGetValue("category", out vs))
{
var category = node.Category;
if (category.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(category))) return false;
if (category.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(category))) return $"[{category}] not Match {vs.Join(",")}";
}

if (Rules.TryGetValue("runtime", out vs))
{
var runtime = node.Runtime;
if (runtime.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(runtime))) return false;
if (runtime.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(runtime))) return $"[{runtime}] not Match {vs.Join(",")}";
}

if (Rules.TryGetValue("framework", out vs))
{
var str = !node.Frameworks.IsNullOrEmpty() ? node.Frameworks : node.Framework;
var frameworks = str?.Split(",");
if (frameworks == null || frameworks.Length == 0) return false;
if (frameworks == null || frameworks.Length == 0) return "Frameworks is null";

// 本节点拥有的所有框架,任意框架匹配任意规则,即可认为匹配
var flag = false;
Expand All @@ -227,35 +231,37 @@ public Boolean Match(Node node, String productCode)
break;
}
}
if (!flag) return false;
if (!flag) return $"[{str}] not Match {vs.Join(",")}";
}
else if (Rules.TryGetValue("framework>", out vs))
{
if (node.Framework.IsNullOrEmpty()) return false;
if (!System.Version.TryParse(node.Framework, out var ver1)) return false;
if (!System.Version.TryParse(vs[0], out var ver2)) return false;
var str = node.Framework;
if (str.IsNullOrEmpty()) return "Version is null";
if (!System.Version.TryParse(str, out var ver1)) return $"Framework=[{str}] is invalid";
if (!System.Version.TryParse(vs[0], out var ver2)) return $"vs[0]=[{vs[0]}] is invalid";

if (ver1 < ver2) return false;
if (ver1 < ver2) return $"Framework=[{ver1}] < {ver2}";
}
else if (Rules.TryGetValue("framework<", out vs))
{
if (node.Framework.IsNullOrEmpty()) return false;
if (!System.Version.TryParse(node.Framework, out var ver1)) return false;
if (!System.Version.TryParse(vs[0], out var ver2)) return false;
var str = node.Framework;
if (str.IsNullOrEmpty()) return "Version is null";
if (!System.Version.TryParse(str, out var ver1)) return $"Framework=[{str}] is invalid";
if (!System.Version.TryParse(vs[0], out var ver2)) return $"vs[0]=[{vs[0]}] is invalid";

if (ver1 > ver2) return false;
if (ver1 > ver2) return $"Framework=[{ver1}] > {ver2}";
}

if (Rules.TryGetValue("os", out vs))
{
var os = node.OS;
if (os.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(os))) return false;
if (os.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(os))) return $"[{os}] not Match {vs.Join(",")}";
}

if (Rules.TryGetValue("oskind", out vs))
{
var os = node.OSKind;
if (os <= 0) return false;
if (os <= 0) return "OSKind is null";

var flag = false;
foreach (var item in vs)
Expand All @@ -271,25 +277,25 @@ public Boolean Match(Node node, String productCode)
break;
}
}
if (!flag) return false;
if (!flag) return $"[{os}] not Match {vs.Join(",")}";
}

if (Rules.TryGetValue("arch", out vs))
{
var arch = node.Architecture;
if (arch.IsNullOrEmpty() || !vs.Any(e => e.EqualIgnoreCase(arch))) return false;
if (arch.IsNullOrEmpty() || !vs.Any(e => e.EqualIgnoreCase(arch))) return $"[{arch}] not Match {vs.Join(",")}";
}

if (Rules.TryGetValue("province", out vs))
{
var province = node.ProvinceID + "";
if (province.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(province))) return false;
if (province.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(province))) return $"[{province}] not Match {vs.Join(",")}";
}

if (Rules.TryGetValue("city", out vs))
{
var city = node.CityID + "";
if (city.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(city))) return false;
if (city.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(city))) return $"[{city}] not Match {vs.Join(",")}";
}

//if (Rules.TryGetValue("product", out vs))
Expand All @@ -298,7 +304,7 @@ public Boolean Match(Node node, String productCode)
// if (product.IsNullOrEmpty() || !vs.Any(e => e.IsMatch(product))) return false;
//}

return true;
return null;
}
#endregion
}
1 change: 1 addition & 0 deletions Stardust.Server/Controllers/NodeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public UpgradeInfo Upgrade(String channel)
{
var node = _node ?? throw new ApiException(401, "节点未登录");

// 基础路径
var uri = Request.GetRawUrl().ToString();
var p = uri.IndexOf('/', "https://".Length);
if (p > 0) uri = uri[..p];
Expand Down
73 changes: 50 additions & 23 deletions Stardust.Server/Services/NodeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,10 @@ public NodeVersion Upgrade(Node node, String channel, String ip)

// 找到所有产品版本
var list = NodeVersion.GetValids(ch);
list = list.Where(e => e.ProductCode.IsNullOrEmpty() || e.ProductCode.EqualIgnoreCase(node.ProductCode)).ToList();
if (list.Count == 0) return null;

using var span = _tracer?.NewSpan(nameof(Upgrade), new { node.Name, node.Code, node.Runtime, node.Framework, node.Frameworks, ip, vers = list.Count });

// 应用过滤规则,使用最新的一个版本
var pv = list.OrderByDescending(e => e.ID).FirstOrDefault(e => e.Version != node.LastVersion && e.Match(node));
Expand All @@ -585,11 +589,11 @@ public NodeVersion Upgrade(Node node, String channel, String ip)
// 检查是否已经升级过这个版本
if (node.LastVersion == pv.Version) return null;

node.WriteHistory("自动更新", true, $"channel={ch} version={node.Version} last={node.LastVersion} => [{pv.ID}] {pv.Version} {pv.Executor}", ip);

node.LastVersion = pv.Version;
node.Update();

node.WriteHistory("自动更新", true, $"channel={ch} version={node.Version} last={node.LastVersion} => [{pv.ID}] {pv.Version} {pv.Executor}", ip);

return pv;
}

Expand All @@ -600,33 +604,56 @@ public NodeVersion Upgrade(Node node, String channel, String ip)
public NodeVersion CheckDotNet(Node node, Uri baseUri, String ip)
{
// 找到所有产品版本
var list = NodeVersion.GetValids(NodeChannels.Release).Where(e => e.ProductCode.EqualIgnoreCase("dotNet")).ToList();
var list = NodeVersion.GetValids(0).Where(e => e.ProductCode.EqualIgnoreCase("dotNet")).ToList();
if (list.Count == 0) return null;

using var span = _tracer?.NewSpan(nameof(CheckDotNet), new { node.Name, node.Code, node.Runtime, node.Framework, node.Frameworks, ip, vers = list.Count });

// 应用过滤规则
list = list.OrderByDescending(e => e.ID).Where(e => e.Match(node)).ToList();
//var list2 = new List<NodeVersion>();
//foreach (var pv in list)
//{
// var rs = pv.MatchResult(node);
// if (rs == null)
// list2.Add(pv);
// else
// span?.AppendTag($"[{pv.Version}] {rs}");
//}
//list = list2;
if (list.Count == 0) return null;

// 每个版本都要检查,如果已经推送,则推送下一个
foreach (var pv in list)
{
span?.AppendTag($"[{pv.ID}]{pv.Version}");

// 应用过滤规则,使用最新的一个版本
var pv = list.OrderByDescending(e => e.ID).FirstOrDefault(e => e.Version != node.LastVersion && e.Match(node, "dotNet"));
if (pv == null) return null;
// 准备安装框架所需要的参数
var fmodel = new FrameworkModel { Version = pv.Version, BaseUrl = pv.Source };
// 如果没有指定源,则使用默认源
if (fmodel.BaseUrl.IsNullOrEmpty()) fmodel.BaseUrl = new Uri(baseUri, "/files/dotnet/").ToString();
span?.AppendTag($" source={fmodel.BaseUrl}");

// 准备安装框架所需要的参数
var fmodel = new FrameworkModel { Version = pv.Version, BaseUrl = pv.Source };
// 如果没有指定源,则使用默认源
if (fmodel.BaseUrl.IsNullOrEmpty()) fmodel.BaseUrl = new Uri(baseUri, "/files/").ToString();
// 检查是否已经升级过这个版本
var key = $"nodeNet:{node.Code}-{fmodel.Version}";
if (_cacheProvider.Cache.Get<String>(key) == pv.Version) return null;
_cacheProvider.Cache.Set(key, pv.Version, 600);

// 检查是否已经升级过这个版本
var key = $"nodeNet:{node.Code}-{fmodel.Version}";
if (_cacheProvider.Cache.Get<String>(key) == pv.Version) return null;
_cacheProvider.Cache.Set(key, pv.Version, 3600);
var model = new CommandInModel
{
Code = node.Code,
Command = "framework/install",
Argument = fmodel.ToJson(),
Expire = 60,
};
_ = SendCommand(node, model, $"NodeVersion:{pv.Version}");

var model = new CommandInModel
{
Code = node.Code,
Command = "framework/install",
Argument = fmodel.ToJson()
};
_ = SendCommand(node, model, "");
node.WriteHistory("推送dotNet", true, $"version={node.Framework} => [{pv.ID}] {pv.Version} {fmodel.BaseUrl}", ip);

node.WriteHistory("推送dotNet", true, $"version={node.Framework} => [{pv.ID}] {pv.Version} {fmodel.BaseUrl}", ip);
return pv;
}

return pv;
return null;
}
#endregion

Expand Down
26 changes: 26 additions & 0 deletions Stardust.ServerTests/Nodes/NodeVersionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,32 @@ public void NodeMatch()

rs = nv.Match(new Node { Code = "6234BE2A", Name = "aml", Version = "2.9.2023.0825" });
Assert.True(rs);

nv = new NodeVersion
{
Strategy = "node=*a2*;framework=6.*,7.*;oskind=ubuntu;version>=2.9.2023.1115",
};
nv.Invoke("OnLoad");

rs = nv.Match(new Node { Code = "6234BE2A", OSKind = OSKinds.Ubuntu, Framework = "6.0", Name = "a2", Version = "2.9.2023.1115" });
Assert.True(rs);
rs = nv.Match(new Node { Code = "6234BE2A", OSKind = OSKinds.Ubuntu, Framework = "6.0", Name = "浇花a2", Version = "2.9.2023.1116" });
Assert.True(rs);
rs = nv.Match(new Node { Code = "6234BE2A", OSKind = OSKinds.Ubuntu, Framework = "7.0", Name = "a2-4g", Version = "2.9.2023.1115" });
Assert.True(rs);
rs = nv.Match(new Node { Code = "6234BE2A", OSKind = OSKinds.Ubuntu, Frameworks = "6.0.21,7.0.10", Name = "a2-4g", Version = "2.9.2023.1114" });
Assert.False(rs);

nv = new NodeVersion
{
Strategy = "node=*a2*;framework<=7.0;oskind=ubuntu;version>=2.9.2023.1115",
};
nv.Invoke("OnLoad");

rs = nv.Match(new Node { Code = "6234BE2A", OSKind = OSKinds.Ubuntu, Framework = "7.0", Name = "a2", Version = "2.9.2023.1115" });
Assert.True(rs);
rs = nv.Match(new Node { Code = "6234BE2A", OSKind = OSKinds.Ubuntu, Framework = "8.0", Name = "a2", Version = "2.9.2023.1115" });
Assert.False(rs);
}

[Fact]
Expand Down

0 comments on commit f6980a7

Please sign in to comment.