Skip to content

Commit eaee6ed

Browse files
committed
merge: integrate origin/metrics-p1-pr936 into sync branch
2 parents 030902f + 5b25f7f commit eaee6ed

File tree

109 files changed

+18231
-732
lines changed

Some content is hidden

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

109 files changed

+18231
-732
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
name: Post-Build Actions (Local)
3+
4+
on:
5+
workflow_call:
6+
inputs:
7+
check-run-id:
8+
type: string
9+
description: "name of the check run"
10+
required: true
11+
check-run-conclusion:
12+
type: string
13+
description: "workflow run status (success|failure|skipped|aborted)"
14+
required: true
15+
16+
jobs:
17+
report-workflow-run:
18+
if: ${{ startsWith(github.event.pull_request.head.repo.full_name, github.repository_owner) && inputs.check-run-conclusion != 'skipped' }}
19+
runs-on: ubuntu-latest
20+
steps:
21+
- id: check-gh-app-secrets
22+
name: Check GH App secrets
23+
env:
24+
GH_APP_ID: ${{ secrets.GH_APP_ID }}
25+
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
26+
run: |
27+
if [ -n "$GH_APP_ID" ] && [ -n "$GH_APP_PRIVATE_KEY" ]; then
28+
echo "enabled=true" >> "$GITHUB_OUTPUT"
29+
else
30+
echo "enabled=false" >> "$GITHUB_OUTPUT"
31+
echo "GH App secrets are missing; skip workflow run report."
32+
fi
33+
- uses: actions/checkout@master
34+
if: ${{ steps.check-gh-app-secrets.outputs.enabled == 'true' }}
35+
with:
36+
repository: daeuniverse/ci-seed-jobs
37+
ref: master
38+
39+
- name: Report workflow run status
40+
if: ${{ steps.check-gh-app-secrets.outputs.enabled == 'true' }}
41+
uses: ./common/report-workflow-run
42+
with:
43+
app_id: ${{ secrets.GH_APP_ID }}
44+
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
45+
id: ${{ inputs.check-run-id }}
46+
conclusion: ${{ inputs.check-run-conclusion }}

.github/workflows/kernel-test.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ jobs:
6060
test-name: dae-test
6161
image-version: ${{ matrix.kernel }}
6262
host-mount: ./
63-
dns-resolver: '1.1.1.1'
6463
install-dependencies: 'true'
6564
cmd: |
6665
chmod +x /host/dae/dae

.github/workflows/pr-build.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ on:
2323
- "go.sum"
2424
- ".github/workflows/pr-build.yml"
2525
- ".github/workflows/seed-build.yml"
26+
- ".github/workflows/pre-actions.local.yml"
27+
- ".github/workflows/dae-post-actions.local.yml"
2628

2729
jobs:
2830
pre-actions:
2931
if: ${{ github.event.pull_request.draft == false }}
30-
uses: daeuniverse/ci-seed-jobs/.github/workflows/pre-actions.yml@master
32+
uses: ./.github/workflows/pre-actions.local.yml
3133
with:
3234
repository: ${{ github.repository }}
3335
ref: ${{ github.event.pull_request.head.sha }}
@@ -38,7 +40,7 @@ jobs:
3840
build:
3941
needs: [pre-actions]
4042
if: ${{ github.event.pull_request.draft == false }}
41-
uses: daeuniverse/dae/.github/workflows/seed-build.yml@main
43+
uses: ./.github/workflows/seed-build.yml
4244
with:
4345
ref: ${{ github.event.pull_request.head.sha }}
4446
pr-number: ${{ github.event.pull_request.number }}
@@ -48,7 +50,7 @@ jobs:
4850
post-actions:
4951
if: always()
5052
needs: [build]
51-
uses: daeuniverse/ci-seed-jobs/.github/workflows/dae-post-actions.yml@master
53+
uses: ./.github/workflows/dae-post-actions.local.yml
5254
with:
5355
check-run-id: "dae-bot[bot]/pr-build-passed"
5456
check-run-conclusion: ${{ needs.build.result }}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
name: Pre-Build Actions (Local)
3+
4+
on:
5+
workflow_call:
6+
inputs:
7+
repository:
8+
required: true
9+
type: string
10+
ref:
11+
required: true
12+
type: string
13+
fetch-depth:
14+
required: false
15+
default: 0
16+
type: string
17+
check-runs:
18+
type: string
19+
required: false
20+
default: "[]"
21+
notify:
22+
type: boolean
23+
default: false
24+
outputs:
25+
git_sha_long:
26+
description: "git sha (long)"
27+
value: ${{ jobs.export-metadata.outputs.git_sha_long }}
28+
git_sha_short:
29+
description: "git sha (short)"
30+
value: ${{ jobs.export-metadata.outputs.git_sha_short }}
31+
git_commit_msg:
32+
description: "git commit message"
33+
value: ${{ jobs.export-metadata.outputs.git_commit_msg }}
34+
35+
jobs:
36+
set-vars:
37+
runs-on: ubuntu-latest
38+
outputs:
39+
check-runs-matrix: ${{ steps.set-check-runs-matrix.outputs.check-runs-matrix }}
40+
steps:
41+
- name: Set check-runs matrix
42+
id: set-check-runs-matrix
43+
run: |
44+
echo "check-runs-matrix=$input" >> $GITHUB_OUTPUT
45+
env:
46+
input: ${{ inputs.check-runs }}
47+
48+
export-metadata:
49+
runs-on: ubuntu-latest
50+
outputs:
51+
git_sha_long: ${{ steps.export.outputs.git_sha_long }}
52+
git_sha_short: ${{ steps.export.outputs.git_sha_short }}
53+
git_commit_msg: ${{ steps.export.outputs.git_commit_msg }}
54+
steps:
55+
- uses: actions/checkout@master
56+
with:
57+
repository: ${{ inputs.repository }}
58+
fetch-depth: ${{ inputs.fetch-depth }}
59+
ref: ${{ inputs.ref }}
60+
- name: Get metadata from HEAD
61+
id: export
62+
run: |
63+
echo "git_sha_long=${{ inputs.ref }}" >> $GITHUB_OUTPUT
64+
echo "git_sha_short=$(echo ${{ inputs.ref }} | cut -c1-6)" >> $GITHUB_OUTPUT
65+
echo "git_commit_msg=$(git log --format=%s -n 1 ${{ inputs.ref }})" >> $GITHUB_OUTPUT
66+
67+
notify-build-start:
68+
if: startsWith(github.event.pull_request.head.repo.full_name, github.repository_owner) && inputs.notify
69+
runs-on: ubuntu-latest
70+
needs: [export-metadata]
71+
steps:
72+
- uses: actions/checkout@master
73+
with:
74+
repository: daeuniverse/ci-seed-jobs
75+
ref: master
76+
- id: send-notification
77+
uses: ./notification/notify-build-start
78+
with:
79+
telegram_to: ${{ secrets.TELEGRAM_TO }}
80+
telegram_token: ${{ secrets.TELEGRAM_TOKEN }}
81+
git_sha_long: ${{ needs.export-metadata.outputs.git_sha_long }}
82+
git_sha_short: ${{ needs.export-metadata.outputs.git_sha_short }}
83+
git_commit_msg: ${{ needs.export-metadata.outputs.git_commit_msg }}
84+
85+
instantiate-check-runs:
86+
needs: [set-vars]
87+
if: startsWith(github.event.pull_request.head.repo.full_name, github.repository_owner)
88+
runs-on: ubuntu-latest
89+
strategy:
90+
fail-fast: false
91+
matrix:
92+
id: ${{ fromJson(needs.set-vars.outputs.check-runs-matrix) }}
93+
steps:
94+
- id: check-gh-app-secrets
95+
name: Check GH App secrets
96+
env:
97+
GH_APP_ID: ${{ secrets.GH_APP_ID }}
98+
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
99+
run: |
100+
if [ -n "$GH_APP_ID" ] && [ -n "$GH_APP_PRIVATE_KEY" ]; then
101+
echo "enabled=true" >> "$GITHUB_OUTPUT"
102+
else
103+
echo "enabled=false" >> "$GITHUB_OUTPUT"
104+
echo "GH App secrets are missing; skip check-run instantiation."
105+
fi
106+
- name: Instantiate required check runs
107+
if: ${{ inputs.check-runs != '[]' && steps.check-gh-app-secrets.outputs.enabled == 'true' }}
108+
uses: daeuniverse/ci-seed-jobs/common/instantiate-check-runs@master
109+
with:
110+
app_id: ${{ secrets.GH_APP_ID }}
111+
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
112+
id: "dae-bot[bot]/${{ matrix.id }}"

.github/workflows/seed-build.yml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,26 @@ jobs:
137137
- name: Upload files to Artifacts
138138
uses: actions/upload-artifact@v4
139139
with:
140-
name: dae-${{ steps.get_filename.outputs.ASSET_NAME }}
140+
name: dae-${{ steps.get_filename.outputs.ASSET_NAME }}.zip
141141
path: build/*
142142

143+
- id: check-gh-app-secrets
144+
name: Check GH App secrets
145+
if: always() && inputs.build-type == 'pr-build' && startsWith(github.event.pull_request.head.repo.full_name, github.repository_owner)
146+
env:
147+
GH_APP_ID: ${{ secrets.GH_APP_ID }}
148+
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
149+
run: |
150+
if [ -n "$GH_APP_ID" ] && [ -n "$GH_APP_PRIVATE_KEY" ]; then
151+
echo "enabled=true" >> "$GITHUB_OUTPUT"
152+
else
153+
echo "enabled=false" >> "$GITHUB_OUTPUT"
154+
echo "GH App secrets are missing; skip check-run report."
155+
fi
156+
143157
- name: Report result
144158
uses: daeuniverse/ci-seed-jobs/common/report-check-run@master
145-
if: always() && inputs.build-type == 'pr-build' && startsWith(github.event.pull_request.head.repo.full_name, github.repository_owner)
159+
if: always() && inputs.build-type == 'pr-build' && startsWith(github.event.pull_request.head.repo.full_name, github.repository_owner) && steps.check-gh-app-secrets.outputs.enabled == 'true'
146160
with:
147161
app_id: ${{ secrets.GH_APP_ID }}
148162
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}

CHANGELOGS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ curl --silent "https://api.github.com/repos/daeuniverse/dae/releases" | jq -r '.
1414

1515
<!-- BEGIN NEW TOC ENTRY -->
1616

17+
- [Unreleased](#unreleased)
1718
- [v1.1.0rc1 (Pre-release)](#v110rc1-pre-release)
1819
- [v1.0.0 (Latest)](#v100-latest)
1920
- [v0.9.0)](#v090)
@@ -48,6 +49,21 @@ curl --silent "https://api.github.com/repos/daeuniverse/dae/releases" | jq -r '.
4849
- [v0.1.0](#v010)
4950
<!-- BEGIN NEW CHANGELOGS -->
5051

52+
### Unreleased
53+
54+
#### Features
55+
56+
- feat(dns): add robust DNS forward fallback path for `tcp+udp` upstream (UDP-first with TCP fallback on request failure).
57+
58+
#### Bug Fixes
59+
60+
- fix(dns): report DNS forward failures to dialer health feedback path to improve failover quality.
61+
- fix(control): harden DNS/UDP connection lifecycle handling in high-concurrency paths.
62+
63+
#### Others
64+
65+
- test(control): add regression tests for DNS fallback, timeout cleanup, and pool concurrency safety.
66+
5167
### v1.1.0rc1 (Pre-release)
5268

5369
> Release date: 2025/11/03

cmd/run.go

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import (
2626
"github.com/daeuniverse/outbound/protocol/direct"
2727
"gopkg.in/natefinch/lumberjack.v2"
2828

29-
_ "net/http/pprof"
30-
3129
"github.com/daeuniverse/dae/cmd/internal"
3230
"github.com/daeuniverse/dae/common"
3331
"github.com/daeuniverse/dae/common/consts"
@@ -37,6 +35,7 @@ import (
3735
"github.com/daeuniverse/dae/control"
3836
"github.com/daeuniverse/dae/pkg/config_parser"
3937
"github.com/daeuniverse/dae/pkg/logger"
38+
"github.com/daeuniverse/dae/pkg/metrics"
4039
"github.com/mohae/deepcopy"
4140
"github.com/okzk/sdnotify"
4241
"github.com/sirupsen/logrus"
@@ -134,12 +133,25 @@ func Run(log *logrus.Logger, conf *config.Config, externGeoDataDirs []string) (e
134133
return err
135134
}
136135

137-
var pprofServer *http.Server
138-
if conf.Global.PprofPort != 0 {
139-
pprofAddr := fmt.Sprintf("localhost:%d", conf.Global.PprofPort)
140-
pprofServer = &http.Server{Addr: pprofAddr, Handler: nil}
141-
go pprofServer.ListenAndServe()
136+
metricsState := metrics.NewState()
137+
metricsState.SetControlPlane(c)
138+
metricsRegistry := metrics.NewRegistry(metricsState)
139+
140+
var endpointServer *http.Server
141+
startEndpointServer := func(cfg metrics.EndpointConfig) {
142+
if cfg.ListenAddress == "" {
143+
endpointServer = nil
144+
return
145+
}
146+
endpointServer = metrics.NewEndpointServer(cfg, metricsRegistry)
147+
go func(server *http.Server, endpointCfg metrics.EndpointConfig) {
148+
if e := metrics.StartEndpointServer(server, endpointCfg); e != nil && !errors.Is(e, http.ErrServerClosed) {
149+
log.WithError(e).Errorln("Endpoint server stopped with error")
150+
}
151+
}(endpointServer, cfg)
142152
}
153+
endpointCfg := endpointConfigFromGlobal(conf, log)
154+
startEndpointServer(endpointCfg)
143155

144156
// Serve tproxy TCP/UDP server util signals.
145157
var listener *control.Listener
@@ -263,7 +275,7 @@ loop:
263275
if err := c.StopDNSListener(); err != nil {
264276
log.Warnf("[Reload] Failed to stop old DNS listener: %v", err)
265277
}
266-
278+
267279
log.Warnln("[Reload] Load new control plane")
268280
newC, err := newControlPlane(log, obj, dnsCache, newConf, externGeoDataDirs)
269281
if err != nil {
@@ -295,21 +307,21 @@ loop:
295307
c = newC
296308
conf = newConf
297309
reloading = true
310+
metricsState.SetControlPlane(newC)
298311

299312
// Ready to close.
300313
if abortConnections {
301314
oldC.AbortConnections()
302315
}
303316
oldC.Close()
304317

305-
if pprofServer != nil {
306-
pprofServer.Shutdown(context.Background())
307-
pprofServer = nil
308-
}
309-
if newConf.Global.PprofPort != 0 {
310-
pprofAddr := fmt.Sprintf("localhost:%d", conf.Global.PprofPort)
311-
pprofServer = &http.Server{Addr: pprofAddr, Handler: nil}
312-
go pprofServer.ListenAndServe()
318+
newEndpointCfg := endpointConfigFromGlobal(newConf, log)
319+
if endpointConfigChanged(endpointCfg, newEndpointCfg) {
320+
if endpointServer != nil {
321+
_ = endpointServer.Shutdown(context.Background())
322+
}
323+
endpointCfg = newEndpointCfg
324+
startEndpointServer(endpointCfg)
313325
}
314326
case syscall.SIGHUP:
315327
// Ignore.
@@ -321,12 +333,37 @@ loop:
321333
}
322334
defer os.Remove(PidFilePath)
323335
defer control.GetDaeNetns().Close()
336+
if endpointServer != nil {
337+
_ = endpointServer.Shutdown(context.Background())
338+
}
324339
if e := c.Close(); e != nil {
325340
return fmt.Errorf("close control plane: %w", e)
326341
}
327342
return nil
328343
}
329344

345+
func endpointConfigFromGlobal(conf *config.Config, log *logrus.Logger) metrics.EndpointConfig {
346+
cfg := metrics.EndpointConfig{
347+
ListenAddress: conf.Global.EndpointListenAddress,
348+
Username: conf.Global.EndpointUsername,
349+
Password: conf.Global.EndpointPassword,
350+
TlsCertificate: conf.Global.EndpointTlsCertificate,
351+
TlsKey: conf.Global.EndpointTlsKey,
352+
PrometheusEnabled: conf.Global.EndpointPrometheusEnabled,
353+
PrometheusPath: conf.Global.EndpointPrometheusPath,
354+
PprofEnabled: conf.Global.PprofPort != 0,
355+
}
356+
if cfg.ListenAddress == "" && conf.Global.PprofPort != 0 {
357+
log.Warnln("pprof_port is deprecated, please use endpoint_listen_address instead")
358+
cfg.ListenAddress = fmt.Sprintf("localhost:%d", conf.Global.PprofPort)
359+
}
360+
return cfg
361+
}
362+
363+
func endpointConfigChanged(a, b metrics.EndpointConfig) bool {
364+
return a != b
365+
}
366+
330367
func newControlPlane(log *logrus.Logger, bpf interface{}, dnsCache map[string]*control.DnsCache, conf *config.Config, externGeoDataDirs []string) (c *control.ControlPlane, err error) {
331368
// Deep copy to prevent modification.
332369
conf = deepcopy.Copy(conf).(*config.Config)

0 commit comments

Comments
 (0)