Skip to content

Commit 22c1a61

Browse files
authored
Merge pull request #19 from MaurUppi/sync/main-pr936-phase1
sync(main): integrate PR936 latest additions and phase1 metrics baseline
2 parents c1c736b + b723023 commit 22c1a61

27 files changed

+1361
-430
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
- uses: actions/checkout@master
22+
with:
23+
repository: daeuniverse/ci-seed-jobs
24+
ref: master
25+
26+
- name: Report workflow run status
27+
uses: ./common/report-workflow-run
28+
with:
29+
app_id: ${{ secrets.GH_APP_ID }}
30+
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
31+
id: ${{ inputs.check-run-id }}
32+
conclusion: ${{ inputs.check-run-conclusion }}

.github/workflows/pr-build.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ on:
2727
jobs:
2828
pre-actions:
2929
if: ${{ github.event.pull_request.draft == false }}
30-
uses: daeuniverse/ci-seed-jobs/.github/workflows/pre-actions.yml@master
30+
uses: ./.github/workflows/pre-actions.local.yml
3131
with:
3232
repository: ${{ github.repository }}
3333
ref: ${{ github.event.pull_request.head.sha }}
@@ -38,7 +38,7 @@ jobs:
3838
build:
3939
needs: [pre-actions]
4040
if: ${{ github.event.pull_request.draft == false }}
41-
uses: daeuniverse/dae/.github/workflows/seed-build.yml@main
41+
uses: ./.github/workflows/seed-build.yml
4242
with:
4343
ref: ${{ github.event.pull_request.head.sha }}
4444
pr-number: ${{ github.event.pull_request.number }}
@@ -48,7 +48,7 @@ jobs:
4848
post-actions:
4949
if: always()
5050
needs: [build]
51-
uses: daeuniverse/ci-seed-jobs/.github/workflows/dae-post-actions.yml@master
51+
uses: ./.github/workflows/dae-post-actions.local.yml
5252
with:
5353
check-run-id: "dae-bot[bot]/pr-build-passed"
5454
check-run-conclusion: ${{ needs.build.result }}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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-github-context:
49+
runs-on: ubuntu-latest
50+
steps:
51+
- name: Dump GitHub context
52+
env:
53+
GITHUB_CONTEXT: ${{ toJson(github) }}
54+
run: |
55+
echo "$GITHUB_CONTEXT"
56+
57+
export-metadata:
58+
runs-on: ubuntu-latest
59+
outputs:
60+
git_sha_long: ${{ steps.export.outputs.git_sha_long }}
61+
git_sha_short: ${{ steps.export.outputs.git_sha_short }}
62+
git_commit_msg: ${{ steps.export.outputs.git_commit_msg }}
63+
steps:
64+
- uses: actions/checkout@master
65+
with:
66+
repository: ${{ inputs.repository }}
67+
fetch-depth: ${{ inputs.fetch-depth }}
68+
ref: ${{ inputs.ref }}
69+
- name: Get metadata from HEAD
70+
id: export
71+
run: |
72+
echo "git_sha_long=${{ inputs.ref }}" >> $GITHUB_OUTPUT
73+
echo "git_sha_short=$(echo ${{ inputs.ref }} | cut -c1-6)" >> $GITHUB_OUTPUT
74+
echo "git_commit_msg=$(git log --format=%s -n 1 ${{ inputs.ref }})" >> $GITHUB_OUTPUT
75+
76+
notify-build-start:
77+
if: startsWith(github.event.pull_request.head.repo.full_name, github.repository_owner) && inputs.notify
78+
runs-on: ubuntu-latest
79+
needs: [export-github-context, export-metadata]
80+
steps:
81+
- uses: actions/checkout@master
82+
with:
83+
repository: daeuniverse/ci-seed-jobs
84+
ref: master
85+
- id: send-notification
86+
uses: ./notification/notify-build-start
87+
with:
88+
telegram_to: ${{ secrets.TELEGRAM_TO }}
89+
telegram_token: ${{ secrets.TELEGRAM_TOKEN }}
90+
git_sha_long: ${{ needs.export-metadata.outputs.git_sha_long }}
91+
git_sha_short: ${{ needs.export-metadata.outputs.git_sha_short }}
92+
git_commit_msg: ${{ needs.export-metadata.outputs.git_commit_msg }}
93+
94+
instantiate-check-runs:
95+
needs: [set-vars]
96+
if: startsWith(github.event.pull_request.head.repo.full_name, github.repository_owner)
97+
runs-on: ubuntu-latest
98+
strategy:
99+
fail-fast: false
100+
matrix:
101+
id: ${{ fromJson(needs.set-vars.outputs.check-runs-matrix) }}
102+
steps:
103+
- name: Instantiate required check runs
104+
if: ${{ inputs.check-runs != '[]' }}
105+
uses: daeuniverse/ci-seed-jobs/common/instantiate-check-runs@master
106+
with:
107+
app_id: ${{ secrets.GH_APP_ID }}
108+
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
109+
id: "dae-bot[bot]/${{ matrix.id }}"

.github/workflows/seed-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ 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

143143
- name: Report result

cmd/run.go

Lines changed: 56 additions & 15 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
@@ -259,6 +271,10 @@ loop:
259271
// Only keep dns cache when ip version preference not change.
260272
dnsCache = c.CloneDnsCache()
261273
}
274+
// Stop old DNS listener before creating new one to avoid port conflicts
275+
if err := c.StopDNSListener(); err != nil {
276+
log.Warnf("[Reload] Failed to stop old DNS listener: %v", err)
277+
}
262278
log.Warnln("[Reload] Load new control plane")
263279
newC, err := newControlPlane(log, obj, dnsCache, newConf, externGeoDataDirs)
264280
if err != nil {
@@ -290,21 +306,21 @@ loop:
290306
c = newC
291307
conf = newConf
292308
reloading = true
309+
metricsState.SetControlPlane(newC)
293310

294311
// Ready to close.
295312
if abortConnections {
296313
oldC.AbortConnections()
297314
}
298315
oldC.Close()
299316

300-
if pprofServer != nil {
301-
pprofServer.Shutdown(context.Background())
302-
pprofServer = nil
303-
}
304-
if newConf.Global.PprofPort != 0 {
305-
pprofAddr := fmt.Sprintf("localhost:%d", conf.Global.PprofPort)
306-
pprofServer = &http.Server{Addr: pprofAddr, Handler: nil}
307-
go pprofServer.ListenAndServe()
317+
newEndpointCfg := endpointConfigFromGlobal(newConf, log)
318+
if endpointConfigChanged(endpointCfg, newEndpointCfg) {
319+
if endpointServer != nil {
320+
_ = endpointServer.Shutdown(context.Background())
321+
}
322+
endpointCfg = newEndpointCfg
323+
startEndpointServer(endpointCfg)
308324
}
309325
case syscall.SIGHUP:
310326
// Ignore.
@@ -316,12 +332,37 @@ loop:
316332
}
317333
defer os.Remove(PidFilePath)
318334
defer control.GetDaeNetns().Close()
335+
if endpointServer != nil {
336+
_ = endpointServer.Shutdown(context.Background())
337+
}
319338
if e := c.Close(); e != nil {
320339
return fmt.Errorf("close control plane: %w", e)
321340
}
322341
return nil
323342
}
324343

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

component/outbound/dialer/alive_dialer_set.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ func (a *AliveDialerSet) GetRand() *Dialer {
102102
return a.inorderedAliveDialerSet[ind]
103103
}
104104

105+
func (a *AliveDialerSet) AliveCount() int {
106+
a.mu.Lock()
107+
defer a.mu.Unlock()
108+
return len(a.inorderedAliveDialerSet)
109+
}
110+
105111
func (a *AliveDialerSet) SortingLatency(d *Dialer) time.Duration {
106112
return a.dialerToLatency[d] + a.dialerToLatencyOffset[d]
107113
}

component/outbound/dialer/connectivity_check.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,18 @@ func (d *Dialer) MustGetLatencies10(typ *NetworkType) *LatenciesN {
503503
return d.mustGetCollection(typ).Latencies10
504504
}
505505

506+
// GetCollectionState returns a snapshot of the dialer's health state for the given network type.
507+
func (d *Dialer) GetCollectionState(typ *NetworkType) (alive bool, lastLatency, avg10, movingAvg time.Duration) {
508+
d.collectionFineMu.Lock()
509+
col := d.mustGetCollection(typ)
510+
alive = col.Alive
511+
movingAvg = col.MovingAverage
512+
d.collectionFineMu.Unlock()
513+
lastLatency, _ = col.Latencies10.LastLatency()
514+
avg10, _ = col.Latencies10.AvgLatency()
515+
return
516+
}
517+
506518
// RegisterAliveDialerSet is thread-safe.
507519
func (d *Dialer) RegisterAliveDialerSet(a *AliveDialerSet) {
508520
if a == nil {

component/outbound/dialer_group.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,17 @@ func (g *DialerGroup) GetSelectionPolicy() (policy consts.DialerSelectionPolicy)
183183
return g.selectionPolicy.Policy
184184
}
185185

186+
func (g *DialerGroup) AliveDialerSets() [6]*dialer.AliveDialerSet {
187+
return g.aliveDialerSets
188+
}
189+
190+
func (g *DialerGroup) SelectionPolicyName() string {
191+
if g.selectionPolicy == nil {
192+
return ""
193+
}
194+
return string(g.selectionPolicy.Policy)
195+
}
196+
186197
func (d *DialerGroup) MustGetAliveDialerSet(typ *dialer.NetworkType) *dialer.AliveDialerSet {
187198
if typ.IsDns {
188199
switch typ.L4Proto {

0 commit comments

Comments
 (0)