Skip to content

Commit 45cf87a

Browse files
committed
feat(caddy): order local machine upstreams first in generated Caddyfile
1 parent 9ce624f commit 45cf87a

2 files changed

Lines changed: 41 additions & 7 deletions

File tree

‎internal/machine/caddyconfig/caddyfile.go‎

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,23 @@ func NewCaddyfileGenerator(
112112
func (g *CaddyfileGenerator) Generate(
113113
ctx context.Context, records []store.ContainerRecord, includeCustom bool,
114114
) (string, error) {
115+
// Sort records by local machine first, then by service name and creation time. Placing containers on the local
116+
// machine first lets user-defined Caddy configs pair this ordering with the "first" lb_policy to always send
117+
// traffic to the same-host replica (skipping the cross-machine hop) and only fall back to remote upstreams when
118+
// the local one is unhealthy.
119+
// The service name and creation time tiebreakers keep the generated Caddyfile stable across regenerations.
120+
slices.SortStableFunc(records, func(a, b store.ContainerRecord) int {
121+
return cmp.Or(
122+
g.localMachineRank(a.MachineID)-g.localMachineRank(b.MachineID),
123+
strings.Compare(a.Container.ServiceName(), b.Container.ServiceName()),
124+
a.Container.CreatedTime().Compare(b.Container.CreatedTime()),
125+
)
126+
})
127+
115128
containers := make([]api.ServiceContainer, len(records))
116129
for i, cr := range records {
117130
containers[i] = cr.Container
118131
}
119-
// Sort containers by service name and creation time to generate a stable Caddyfile.
120-
slices.SortStableFunc(containers, func(a, b api.ServiceContainer) int {
121-
return cmp.Or(
122-
strings.Compare(a.ServiceName(), b.ServiceName()),
123-
a.CreatedTime().Compare(b.CreatedTime()),
124-
)
125-
})
126132

127133
caddyfile, err := g.generateBaseFromPorts(containers)
128134
if err != nil {
@@ -243,6 +249,15 @@ func (g *CaddyfileGenerator) Generate(
243249
return caddyfileHeader + "\n" + caddyfile, nil
244250
}
245251

252+
// localMachineRank returns 0 if the given machineID matches the local machine and 1 otherwise.
253+
// Useful for sorting containers running locally first.
254+
func (g *CaddyfileGenerator) localMachineRank(machineID string) int {
255+
if g.machineID == machineID {
256+
return 0
257+
}
258+
return 1
259+
}
260+
246261
func (g *CaddyfileGenerator) generateBaseFromPorts(containers []api.ServiceContainer) (string, error) {
247262
httpHostUpstreams, httpsHostUpstreams := httpUpstreamsFromPorts(containers)
248263

‎internal/machine/caddyconfig/caddyfile_test.go‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,25 @@ https://secure.example.com {
159159
}
160160
log
161161
}
162+
`,
163+
},
164+
{
165+
name: "local machine upstreams listed first",
166+
containers: []store.ContainerRecord{
167+
newContainerRecord(newContainer("10.210.0.2", "app.example.com:8080/http"), "mach1"),
168+
newContainerRecord(newContainer("10.210.1.2", "app.example.com:8080/http"), "test-machine-id"),
169+
newContainerRecord(newContainer("10.210.2.2", "app.example.com:8080/http"), "mach2"),
170+
newContainerRecord(newContainer("10.210.1.3", "app.example.com:8080/http"), "test-machine-id"),
171+
},
172+
want: testCaddyfileHeader + `
173+
# Sites generated from service ports.
174+
175+
http://app.example.com {
176+
reverse_proxy 10.210.1.2:8080 10.210.1.3:8080 10.210.0.2:8080 10.210.2.2:8080 {
177+
import common_proxy
178+
}
179+
log
180+
}
162181
`,
163182
},
164183
{

0 commit comments

Comments
 (0)