Skip to content

Commit c70e377

Browse files
author
root
committed
fix monitor
1 parent 1e51388 commit c70e377

50 files changed

Lines changed: 2905 additions & 528 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ redo:
222222
@docker rm -f $$(docker ps -aq --filter name=$(CONTAINER)) 2>/dev/null || true
223223
@$(COMPOSE_CMD) down --timeout 5 -v 2>/dev/null || true
224224
@echo "✓ Container and volumes removed"
225+
@if grep -q '^APPOS_SECRET_KEY=replace-with-a-random-base64-secret' build/.env 2>/dev/null; then \
226+
NEW_KEY=$$(openssl rand -base64 32); \
227+
sed -i "s|^APPOS_SECRET_KEY=replace-with-a-random-base64-secret|APPOS_SECRET_KEY=$$NEW_KEY|" build/.env; \
228+
echo "✓ Generated APPOS_SECRET_KEY in build/.env"; \
229+
fi
225230
@$(MAKE) build
226231
@$(MAKE) image build-local
227232
@$(MAKE) start dev
@@ -894,6 +899,11 @@ endif
894899
# Container Management
895900
# ============================================================
896901
start:
902+
@if grep -q '^APPOS_SECRET_KEY=replace-with-a-random-base64-secret' build/.env 2>/dev/null; then \
903+
NEW_KEY=$$(openssl rand -base64 32); \
904+
sed -i "s|^APPOS_SECRET_KEY=replace-with-a-random-base64-secret|APPOS_SECRET_KEY=$$NEW_KEY|" build/.env; \
905+
echo "✓ Generated APPOS_SECRET_KEY in build/.env"; \
906+
fi
897907
@if [ "$(ARG2)" = "dev" ] || [ "$(ARG2)" = "latest" ]; then \
898908
IMAGE_TAG=$(ARG2); \
899909
PORT=9091; \

Note.md

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -154,19 +154,6 @@ Docker tab 的 container
154154
2. 分页 <> 中的页号与分页操作图标之间建议保持一个字符的距离即可
155155

156156

157-
Components 的 Addos 优化:
158-
159-
1. version 列显示两个版本:
160-
Target version
161-
Current version
162-
没有安装时,仅显示 target version
163-
164-
2. 增加一列表示安装制品格式,包含:package, binary, docker 等
165-
3. Component 列表格内容,增加问号,点击查看这个组件的介绍。
166-
3. Status 列应该表示这个组件的真实运行态,installed 其实不属于这个态。
167-
168-
169-
170157

171158

172159
ports tab 不稳定,经常打开显示 somethins wrong

backend/domain/monitor/metrics/query_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,3 +410,33 @@ func TestQueryMetricSeriesRejectsPartialCustomRange(t *testing.T) {
410410
t.Fatalf("expected partial custom range rejection, got %v", err)
411411
}
412412
}
413+
414+
func TestResolveMetricSeriesWindowAlignsFixedWindowToStepBoundary(t *testing.T) {
415+
now := time.Date(2026, time.May, 14, 10, 7, 23, 0, time.UTC)
416+
start, end, step, err := metrics.ResolveMetricSeriesWindowForTest("24h", metrics.MetricSeriesQueryOptions{}, now)
417+
if err != nil {
418+
t.Fatal(err)
419+
}
420+
wantEnd := time.Date(2026, time.May, 14, 10, 0, 0, 0, time.UTC)
421+
if !end.Equal(wantEnd) {
422+
t.Fatalf("expected aligned end %s, got %s", wantEnd, end)
423+
}
424+
if got := end.Sub(start); got != 24*time.Hour {
425+
t.Fatalf("expected 24h duration, got %s", got)
426+
}
427+
if step != 15*time.Minute {
428+
t.Fatalf("expected 15m step, got %s", step)
429+
}
430+
431+
_, end7d, step7d, err := metrics.ResolveMetricSeriesWindowForTest("7d", metrics.MetricSeriesQueryOptions{}, now)
432+
if err != nil {
433+
t.Fatal(err)
434+
}
435+
want7dEnd := time.Date(2026, time.May, 14, 10, 0, 0, 0, time.UTC)
436+
if !end7d.Equal(want7dEnd) {
437+
t.Fatalf("expected aligned 7d end %s, got %s", want7dEnd, end7d)
438+
}
439+
if step7d != time.Hour {
440+
t.Fatalf("expected 1h step, got %s", step7d)
441+
}
442+
}

backend/domain/monitor/metrics/query_window.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func resolveMetricSeriesWindow(window string, options MetricSeriesQueryOptions,
4848
if !ok {
4949
return metricSeriesWindowSpec{}, fmt.Errorf("window %q is not allowed", window)
5050
}
51-
end := now.UTC()
51+
end := alignTimeToStepBoundary(now.UTC(), windowSpec.Step)
5252
start := end.Add(-windowSpec.Duration)
5353
return metricSeriesWindowSpec{
5454
Label: window,
@@ -58,6 +58,18 @@ func resolveMetricSeriesWindow(window string, options MetricSeriesQueryOptions,
5858
}, nil
5959
}
6060

61+
func alignTimeToStepBoundary(value time.Time, step time.Duration) time.Time {
62+
if step <= 0 {
63+
return value
64+
}
65+
unix := value.Unix()
66+
stepSeconds := int64(step / time.Second)
67+
if stepSeconds <= 0 {
68+
return value
69+
}
70+
return time.Unix((unix/stepSeconds)*stepSeconds, 0).UTC()
71+
}
72+
6173
func stepForSeriesDuration(duration time.Duration) time.Duration {
6274
switch {
6375
case duration <= time.Hour:
@@ -118,3 +130,12 @@ func normalizeRequestedSeries(seriesNames []string) []string {
118130
}
119131
return normalized
120132
}
133+
134+
// ResolveMetricSeriesWindowForTest exposes fixed-window resolution to external tests.
135+
func ResolveMetricSeriesWindowForTest(window string, options MetricSeriesQueryOptions, now time.Time) (time.Time, time.Time, time.Duration, error) {
136+
spec, err := resolveMetricSeriesWindow(window, options, now)
137+
if err != nil {
138+
return time.Time{}, time.Time{}, 0, err
139+
}
140+
return spec.Start, spec.End, spec.Step, nil
141+
}

backend/domain/monitor/signals/checks/server_facts_pull.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func ParseServerFactsCommandOutput(output string) (map[string]any, error) {
144144
if err != nil || memoryTotalBytes <= 0 {
145145
return nil, fmt.Errorf("invalid memory.total_bytes %q", values["memory.total_bytes"])
146146
}
147-
return map[string]any{
147+
facts := map[string]any{
148148
"os": map[string]any{
149149
"family": values["os.family"],
150150
"distribution": values["os.distribution"],
@@ -160,7 +160,25 @@ func ParseServerFactsCommandOutput(output string) (map[string]any, error) {
160160
"memory": map[string]any{
161161
"total_bytes": memoryTotalBytes,
162162
},
163-
}, nil
163+
}
164+
165+
if provider := normalizeCloudFactValue(values["cloud.provider"]); provider != "" {
166+
cloudFacts := map[string]any{
167+
"provider": provider,
168+
}
169+
if region := normalizeCloudFactValue(values["cloud.region"]); region != "" {
170+
cloudFacts["region"] = region
171+
}
172+
if zone := normalizeCloudFactValue(values["cloud.zone"]); zone != "" {
173+
cloudFacts["zone"] = zone
174+
}
175+
if source := normalizeCloudFactValue(values["cloud.source"]); source != "" {
176+
cloudFacts["source"] = source
177+
}
178+
facts["cloud"] = cloudFacts
179+
}
180+
181+
return facts, nil
164182
}
165183

166184
func serverFactsCommand() string {
@@ -176,12 +194,37 @@ func serverFactsCommand() string {
176194
"architecture=$(uname -m 2>/dev/null || printf unknown)",
177195
"cpu_cores=$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc 2>/dev/null || printf 1)",
178196
"memory_total_bytes=$(awk '/^MemTotal:/ {printf \"%d\", $2 * 1024}' /proc/meminfo 2>/dev/null || printf 1)",
197+
"cloud_provider=",
198+
"cloud_region=",
199+
"cloud_zone=",
200+
"cloud_source=",
201+
"if command -v cloud-init >/dev/null 2>&1; then cloud_provider=$(cloud-init query cloud_name 2>/dev/null || printf ''); fi",
202+
"if [ -z \"$cloud_provider\" ] && command -v cloud-init >/dev/null 2>&1; then cloud_provider=$(cloud-init query cloud_id 2>/dev/null || printf ''); fi",
203+
"if command -v cloud-init >/dev/null 2>&1; then cloud_region=$(cloud-init query region 2>/dev/null || printf ''); fi",
204+
"if command -v cloud-init >/dev/null 2>&1; then cloud_zone=$(cloud-init query availability_zone 2>/dev/null || printf ''); fi",
205+
"if [ -z \"$cloud_zone\" ] && command -v cloud-init >/dev/null 2>&1; then cloud_zone=$(cloud-init query availability-zone 2>/dev/null || printf ''); fi",
206+
"if [ -z \"$cloud_region\" ] && [ -n \"$cloud_zone\" ]; then cloud_region=$(printf '%s' \"$cloud_zone\" | sed 's/[[:alpha:]]$//'); fi",
207+
"if [ -n \"$cloud_provider\" ]; then cloud_source=cloud-init; fi",
179208
"printf 'os.family=%s\\n' \"$os_family\"",
180209
"printf 'os.distribution=%s\\n' \"$os_distribution\"",
181210
"printf 'os.version=%s\\n' \"$os_version\"",
182211
"printf 'kernel.release=%s\\n' \"$kernel_release\"",
183212
"printf 'architecture=%s\\n' \"$architecture\"",
184213
"printf 'cpu.cores=%s\\n' \"$cpu_cores\"",
185214
"printf 'memory.total_bytes=%s\\n' \"$memory_total_bytes\"",
215+
"if [ -n \"$cloud_provider\" ]; then printf 'cloud.provider=%s\\n' \"$cloud_provider\"; fi",
216+
"if [ -n \"$cloud_region\" ]; then printf 'cloud.region=%s\\n' \"$cloud_region\"; fi",
217+
"if [ -n \"$cloud_zone\" ]; then printf 'cloud.zone=%s\\n' \"$cloud_zone\"; fi",
218+
"if [ -n \"$cloud_source\" ]; then printf 'cloud.source=%s\\n' \"$cloud_source\"; fi",
186219
}, " && ")
187220
}
221+
222+
func normalizeCloudFactValue(value string) string {
223+
text := strings.TrimSpace(value)
224+
switch strings.ToLower(text) {
225+
case "", "<nil>", "null", "(null)", "undefined", "(undefined)":
226+
return ""
227+
default:
228+
return text
229+
}
230+
}

backend/domain/monitor/signals/checks/server_facts_pull_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ kernel.release=6.8.0-31-generic
1010
architecture=x86_64
1111
cpu.cores=4
1212
memory.total_bytes=8589934592
13+
cloud.provider=aws
14+
cloud.region=cn-northwest-1
15+
cloud.zone=cn-northwest-1a
16+
cloud.source=cloud-init
1317
`)
1418
if err != nil {
1519
t.Fatal(err)
@@ -26,6 +30,10 @@ memory.total_bytes=8589934592
2630
if memoryFacts["total_bytes"] != int64(8589934592) {
2731
t.Fatalf("expected memory total bytes, got %+v", facts)
2832
}
33+
cloudFacts := facts["cloud"].(map[string]any)
34+
if cloudFacts["provider"] != "aws" || cloudFacts["region"] != "cn-northwest-1" || cloudFacts["zone"] != "cn-northwest-1a" || cloudFacts["source"] != "cloud-init" {
35+
t.Fatalf("expected cloud facts, got %+v", facts)
36+
}
2937
}
3038

3139
func TestParseServerFactsCommandOutputRejectsMissingRequiredField(t *testing.T) {
@@ -40,3 +48,21 @@ cpu.cores=4
4048
t.Fatal("expected missing memory.total_bytes error")
4149
}
4250
}
51+
52+
func TestParseServerFactsCommandOutputTreatsCloudFieldsAsOptional(t *testing.T) {
53+
facts, err := ParseServerFactsCommandOutput(`os.family=Linux
54+
os.distribution=Ubuntu
55+
os.version=24.04
56+
kernel.release=6.8.0-31-generic
57+
architecture=x86_64
58+
cpu.cores=4
59+
memory.total_bytes=8589934592
60+
cloud.provider=
61+
`)
62+
if err != nil {
63+
t.Fatal(err)
64+
}
65+
if _, ok := facts["cloud"]; ok {
66+
t.Fatalf("expected no cloud facts group, got %+v", facts)
67+
}
68+
}

backend/domain/monitor/signals/snapshots/facts.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ func normalizeFactsSnapshot(facts map[string]any) (map[string]any, error) {
9494
return nil, err
9595
}
9696
normalized["memory"] = group
97+
case "cloud":
98+
group, err := normalizeCloudFacts(value)
99+
if err != nil {
100+
return nil, err
101+
}
102+
normalized["cloud"] = group
97103
default:
98104
return nil, fmt.Errorf("unknown facts group %q", key)
99105
}
@@ -197,6 +203,36 @@ func normalizeMemoryFacts(value any) (map[string]any, error) {
197203
return normalized, nil
198204
}
199205

206+
func normalizeCloudFacts(value any) (map[string]any, error) {
207+
group, err := requireMap(value, "cloud")
208+
if err != nil {
209+
return nil, err
210+
}
211+
normalized := make(map[string]any, len(group))
212+
for key, nested := range group {
213+
switch strings.TrimSpace(key) {
214+
case "provider":
215+
text, err := normalizeRequiredString(nested, "cloud.provider")
216+
if err != nil {
217+
return nil, err
218+
}
219+
normalized[key] = text
220+
case "region", "zone", "source":
221+
text, err := normalizeRequiredString(nested, "cloud."+key)
222+
if err != nil {
223+
return nil, err
224+
}
225+
normalized[key] = text
226+
default:
227+
return nil, fmt.Errorf("unknown facts field %q", "cloud."+key)
228+
}
229+
}
230+
if strings.TrimSpace(fmt.Sprint(normalized["provider"])) == "" {
231+
return nil, fmt.Errorf("cloud.provider must be a non-empty string")
232+
}
233+
return normalized, nil
234+
}
235+
200236
func requireMap(value any, field string) (map[string]any, error) {
201237
group, ok := value.(map[string]any)
202238
if !ok {

0 commit comments

Comments
 (0)