Skip to content

Commit c544126

Browse files
authored
Merge pull request #2 from fishwww-ww/feature/mongodb-template
feat(template): add mongodb protocol template
2 parents a6e7051 + ae309cc commit c544126

12 files changed

Lines changed: 84 additions & 13 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Sealtun 是一款面向 **Sealos Cloud** 和 **Kubernetes** 用户的本地隧
2020
- 📊 **状态、诊断与工作台**`doctor <tunnel-id>``inspect --remote``logs``events``metrics``dashboard` 可定位本地端口、daemon、远端 Pod、Service、Ingress 与证书问题,也可在本地工作台中管理隧道。
2121
- 🧭 **引导与自动修复**`init` 可根据登录状态和本地监听端口推荐命令/YAML;`resources``watch``doctor --fix --dry-run` 可帮助理解和保守修复隧道状态。
2222
- 🔌 **集群内服务访问**:Linux 下 `sudo sealtun connect` 可直接访问 Service FQDN、Service ClusterIP 和 Pod IP 的 TCP 流量,无需 SOCKS 或客户端代理配置。
23-
- 🧩 **协议模板**`template https|ssh|tcp|mysql|postgres|redis|mqtt` 可生成直接命令和 `sealtun.yaml` 示例。
23+
- 🧩 **协议模板**`template https|ssh|tcp|mysql|postgres|redis|mongodb|mqtt` 可生成直接命令和 `sealtun.yaml` 示例。
2424
- 🧾 **声明式配置**`apply -f sealtun.yaml` 可用 YAML 声明隧道,并以稳定名称幂等创建或更新;`export` 可把本地 session 导出回 YAML。
2525
- 🌐 **深度适配 Sealos**:原生使用 Sealos Cloud 的域名、证书和 Kubernetes 资源能力。
2626

@@ -482,9 +482,10 @@ sealtun template https --name web --port 3000 --domain app.example.com
482482
sealtun template ssh
483483
sealtun template postgres
484484
sealtun template redis --name cache
485+
sealtun template mongodb
485486
```
486487

487-
模板会同时输出一次性 `sealtun expose` 命令和可提交到项目内的 `sealtun.yaml` 片段。`mysql``postgres``redis``mqtt` 模板默认走通用 TCP 四层入口;HTTPS 模板才支持自定义域名和访问控制。
488+
模板会同时输出一次性 `sealtun expose` 命令和可提交到项目内的 `sealtun.yaml` 片段。`mysql``postgres``redis``mongodb``mqtt` 模板默认走通用 TCP 四层入口;HTTPS 模板才支持自定义域名和访问控制。
488489

489490
### 9. 声明式配置
490491
创建 `sealtun.yaml`

README_EN.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Sealtun is a local tunnel CLI for **Sealos Cloud** and **Kubernetes** users. It
2020
- 📊 **Status, Diagnostics, and Workbench**: Use `doctor <tunnel-id>`, `inspect --remote`, `logs`, `events`, `metrics`, and `dashboard` to diagnose local ports, daemon state, remote Pods, Services, Ingresses, and certificates, or manage tunnels from the local workbench.
2121
- 🧭 **Guided UX and Safe Fixes**: Use `init` for first-run command/YAML recommendations, and `resources`, `watch`, or `doctor --fix --dry-run` to understand and conservatively repair tunnel state.
2222
- 🔌 **Cluster Service Access**: On Linux, `sudo sealtun connect` lets TCP clients directly reach Service FQDNs, Service ClusterIPs, and Pod IPs without SOCKS or client-side proxy config.
23-
- 🧩 **Protocol Templates**: Use `template https|ssh|tcp|mysql|postgres|redis|mqtt` to generate commands and `sealtun.yaml` examples.
23+
- 🧩 **Protocol Templates**: Use `template https|ssh|tcp|mysql|postgres|redis|mongodb|mqtt` to generate commands and `sealtun.yaml` examples.
2424
- 🧾 **Declarative Config**: Use `apply -f sealtun.yaml` to declare tunnels in YAML and create or update them with stable names; use `export` to turn local sessions back into YAML.
2525
- 🌐 **Optimized for Sealos**: Native support for Sealos Cloud domains, certificates, and Kubernetes resources.
2626

@@ -482,9 +482,10 @@ sealtun template https --name web --port 3000 --domain app.example.com
482482
sealtun template ssh
483483
sealtun template postgres
484484
sealtun template redis --name cache
485+
sealtun template mongodb
485486
```
486487

487-
Templates print both a one-shot `sealtun expose` command and a `sealtun.yaml` snippet. `mysql`, `postgres`, `redis`, and `mqtt` templates default to generic TCP L4 entries; only HTTPS templates support custom domains and access controls.
488+
Templates print both a one-shot `sealtun expose` command and a `sealtun.yaml` snippet. `mysql`, `postgres`, `redis`, `mongodb`, and `mqtt` templates default to generic TCP L4 entries; only HTTPS templates support custom domains and access controls.
488489

489490
### 9. Declarative config
490491
Create `sealtun.yaml`:

cmd/dashboard.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,6 +1633,7 @@ var dashboardHTML = template.Must(template.New("dashboard").Parse(`<!doctype htm
16331633
mysql: { name: "mysql", port: 3306, protocol: "tcp" },
16341634
postgres: { name: "postgres", port: 5432, protocol: "tcp" },
16351635
redis: { name: "redis", port: 6379, protocol: "tcp" },
1636+
mongodb: { name: "mongodb", port: 27017, protocol: "tcp" },
16361637
mqtt: { name: "mqtt", port: 1883, protocol: "tcp" }
16371638
};
16381639

cmd/dashboard_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,33 @@ func TestDashboardDiscoverReturnsProviderResults(t *testing.T) {
188188
}
189189
}
190190

191+
func TestDashboardDiscoverReturnsMongoDBHint(t *testing.T) {
192+
previous := dashboardPortDiscoverer
193+
dashboardPortDiscoverer = fakePortDiscoverer{items: []discoverItem{{Port: 27017, Address: "127.0.0.1", PID: 123, ProcessName: "mongod"}}}
194+
t.Cleanup(func() { dashboardPortDiscoverer = previous })
195+
196+
req := httptest.NewRequest(http.MethodGet, "/api/discover?limit=1", nil)
197+
req.Header.Set("X-Sealtun-Dashboard-Token", "secret")
198+
rec := httptest.NewRecorder()
199+
dashboardServer{token: "secret"}.serveAPI(rec, req)
200+
201+
if rec.Code != http.StatusOK {
202+
t.Fatalf("expected 200, got %d body=%s", rec.Code, rec.Body.String())
203+
}
204+
var payload dashboardAPIResponse
205+
if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil {
206+
t.Fatal(err)
207+
}
208+
data, ok := payload.Data.([]interface{})
209+
if !ok || len(data) != 1 {
210+
t.Fatalf("expected one discovery item, got %#v", payload.Data)
211+
}
212+
item, ok := data[0].(map[string]interface{})
213+
if !ok || item["templateHint"] != "mongodb" || item["protocolHint"] != "tcp" {
214+
t.Fatalf("unexpected discovery payload: %#v", payload.Data)
215+
}
216+
}
217+
191218
func TestDashboardWatchRequiresToken(t *testing.T) {
192219
t.Parallel()
193220

cmd/discover.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ func applyPortHints(item discoverItem) discoverItem {
139139
item.ProtocolHint, item.TemplateHint, item.Confidence = tunnelprotocol.TCP, "postgres", 0.95
140140
case 6379:
141141
item.ProtocolHint, item.TemplateHint, item.Confidence = tunnelprotocol.TCP, "redis", 0.95
142+
case 27017:
143+
item.ProtocolHint, item.TemplateHint, item.Confidence = tunnelprotocol.TCP, "mongodb", 0.95
142144
case 1883:
143145
item.ProtocolHint, item.TemplateHint, item.Confidence = tunnelprotocol.TCP, "mqtt", 0.95
144146
default:

cmd/discover_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func TestDiscoverLocalPortsAppliesProtocolTemplateHints(t *testing.T) {
2424
{Port: 3306, Address: "127.0.0.1"},
2525
{Port: 5432, Address: "127.0.0.1"},
2626
{Port: 6379, Address: "127.0.0.1"},
27+
{Port: 27017, Address: "127.0.0.1"},
2728
{Port: 1883, Address: "127.0.0.1"},
2829
{Port: 3000, Address: "127.0.0.1"},
2930
}})
@@ -54,6 +55,9 @@ func TestDiscoverLocalPortsAppliesProtocolTemplateHints(t *testing.T) {
5455
t.Fatalf("unexpected hint for %d: %#v", port, item)
5556
}
5657
}
58+
if item, ok := got[27017]; !ok || item.ProtocolHint != "tcp" || item.TemplateHint != "mongodb" || item.Command == "" {
59+
t.Fatalf("unexpected mongodb hint: %#v", item)
60+
}
5761
}
5862

5963
func TestDiscoverLocalPortsFiltersAndLimits(t *testing.T) {

cmd/init.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func init() {
7979
rootCmd.AddCommand(initCmd)
8080
initCmd.Flags().BoolVar(&initOpts.JSON, "json", false, "Output onboarding recommendation as JSON")
8181
initCmd.Flags().BoolVar(&initOpts.Apply, "apply", false, "Create the recommended tunnel")
82-
initCmd.Flags().StringVar(&initOpts.Protocol, "protocol", "auto", "Recommended protocol: auto, https, ssh, tcp, mysql, postgres, redis, or mqtt")
82+
initCmd.Flags().StringVar(&initOpts.Protocol, "protocol", "auto", "Recommended protocol: auto, https, ssh, tcp, mysql, postgres, redis, mongodb, or mqtt")
8383
initCmd.Flags().IntVar(&initOpts.Port, "port", 0, "Local port to use for the recommendation")
8484
initCmd.Flags().StringVar(&initOpts.Name, "name", "", "Tunnel name for the generated sealtun.yaml")
8585
initCmd.Flags().StringVar(&initOpts.Domain, "domain", "", "Custom domain for an HTTPS recommendation")
@@ -139,7 +139,7 @@ func initRecommendationFromOptions(opts initOptions, ports []discoverItem) (init
139139
}
140140
spec, ok := protocolTemplateSpec(kind)
141141
if !ok {
142-
return initRecommendation{}, fmt.Errorf("unsupported protocol %q; use auto, https, ssh, tcp, mysql, postgres, redis, or mqtt", opts.Protocol)
142+
return initRecommendation{}, fmt.Errorf("unsupported protocol %q; use auto, https, ssh, tcp, mysql, postgres, redis, mongodb, or mqtt", opts.Protocol)
143143
}
144144
templateKind := canonicalInitTemplateKind(kind, spec)
145145
if explicitKind && opts.Port == 0 {

cmd/init_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ func TestInitExplicitProtocolUsesTemplatePortUnlessPortProvided(t *testing.T) {
9090
if rec.Template != "postgres" || rec.Protocol != "tcp" || rec.LocalPort != 5432 {
9191
t.Fatalf("explicit postgres should use template default, got %#v", rec)
9292
}
93+
94+
rec, err = initRecommendationFromOptions(initOptions{Protocol: "mongodb"}, []discoverItem{{Port: 1082, TemplateHint: "https", ProtocolHint: "https"}})
95+
if err != nil {
96+
t.Fatal(err)
97+
}
98+
if rec.Template != "mongodb" || rec.Protocol != "tcp" || rec.LocalPort != 27017 {
99+
t.Fatalf("explicit mongodb should use template default, got %#v", rec)
100+
}
101+
93102
rec, err = initRecommendationFromOptions(initOptions{Protocol: "postgres", Port: 15432}, []discoverItem{{Port: 1082, TemplateHint: "https", ProtocolHint: "https"}})
94103
if err != nil {
95104
t.Fatal(err)

cmd/template.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var templatePort int
2525
var templateDomain string
2626

2727
var templateCmd = &cobra.Command{
28-
Use: "template [https|ssh|tcp|mysql|postgres|redis|mqtt]",
28+
Use: "template [https|ssh|tcp|mysql|postgres|redis|mongodb|mqtt]",
2929
Short: "Generate protocol-specific Sealtun command and YAML templates",
3030
Args: cobra.ExactArgs(1),
3131
SilenceUsage: true,
@@ -56,7 +56,7 @@ func buildProtocolTemplate(kind, name string, port int, domain string) (*protoco
5656
kind = strings.ToLower(strings.TrimSpace(kind))
5757
spec, ok := protocolTemplateSpec(kind)
5858
if !ok {
59-
return nil, fmt.Errorf("unsupported template %q; use https, ssh, tcp, mysql, postgres, redis, or mqtt", kind)
59+
return nil, fmt.Errorf("unsupported template %q; use https, ssh, tcp, mysql, postgres, redis, mongodb, or mqtt", kind)
6060
}
6161
if name == "" {
6262
name = spec.name
@@ -133,6 +133,8 @@ func protocolTemplateSpec(kind string) (templateSpec, bool) {
133133
return templateSpec{name: "postgres", port: 5432, protocol: tunnelprotocol.TCP, description: "Expose a local PostgreSQL service over raw TCP."}, true
134134
case "redis":
135135
return templateSpec{name: "redis", port: 6379, protocol: tunnelprotocol.TCP, description: "Expose a local Redis service over raw TCP."}, true
136+
case "mongodb":
137+
return templateSpec{name: "mongodb", port: 27017, protocol: tunnelprotocol.TCP, description: "Expose a local MongoDB service over raw TCP."}, true
136138
case "mqtt":
137139
return templateSpec{name: "mqtt", port: 1883, protocol: tunnelprotocol.TCP, description: "Expose a local MQTT broker over raw TCP."}, true
138140
default:

cmd/template_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,24 @@ func TestBuildProtocolTemplateForPostgres(t *testing.T) {
2121
}
2222
}
2323

24+
func TestBuildProtocolTemplateForMongoDB(t *testing.T) {
25+
tmpl, err := buildProtocolTemplate("mongodb", "", 0, "")
26+
if err != nil {
27+
t.Fatalf("buildProtocolTemplate returned error: %v", err)
28+
}
29+
if tmpl.Protocol != "tcp" || tmpl.Name != "mongodb" || tmpl.LocalPort != 27017 {
30+
t.Fatalf("unexpected mongodb template: %#v", tmpl)
31+
}
32+
if !strings.Contains(tmpl.Command, "sealtun expose 27017 --protocol tcp") {
33+
t.Fatalf("unexpected command: %s", tmpl.Command)
34+
}
35+
for _, want := range []string{"name: mongodb", "protocol: tcp", "localPort: 27017"} {
36+
if !strings.Contains(tmpl.Config, want) {
37+
t.Fatalf("expected %q in YAML, got:\n%s", want, tmpl.Config)
38+
}
39+
}
40+
}
41+
2442
func TestBuildProtocolTemplateForHTTPSDomain(t *testing.T) {
2543
tmpl, err := buildProtocolTemplate("https", "api", 8080, "API.Example.COM.")
2644
if err != nil {
@@ -39,6 +57,11 @@ func TestBuildProtocolTemplateRejectsDomainForTCP(t *testing.T) {
3957
if err == nil || !strings.Contains(err.Error(), "only supported for https") {
4058
t.Fatalf("expected tcp domain rejection, got %v", err)
4159
}
60+
61+
_, err = buildProtocolTemplate("mongodb", "", 0, "db.example.com")
62+
if err == nil || !strings.Contains(err.Error(), "only supported for https") {
63+
t.Fatalf("expected mongodb domain rejection, got %v", err)
64+
}
4265
}
4366

4467
func TestBuildProtocolTemplateRejectsUnknownKind(t *testing.T) {

0 commit comments

Comments
 (0)