Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions pkg/api/types_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package api

// Service represents a published service in API7 EE (runtime).
type Service struct {
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Desc string `json:"desc,omitempty" yaml:"desc,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Desc string `json:"desc,omitempty" yaml:"desc,omitempty"`
// Type marks an L4 service ("stream"); absent/"http" for L7. Stream routes
// can only bind to stream-typed services, so it must survive export.
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
Comment thread
shreemaan-abhishek marked this conversation as resolved.
Upstream map[string]interface{} `json:"upstream,omitempty" yaml:"upstream,omitempty"`
UpstreamID string `json:"upstream_id,omitempty" yaml:"upstream_id,omitempty"`
Expand Down
38 changes: 38 additions & 0 deletions pkg/cmd/config/dump/dump_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,41 @@ func TestConfigDump_StreamRoutesDisabled(t *testing.T) {
assert.NotContains(t, result, "stream_routes")
reg.Verify(t)
}

// TestConfigDump_L4ServiceKeepsTypeAndPlugins guards against the export
// dropping L4 (stream) service configuration. A stream service is marked by
// `type: stream`; without it the service re-imports as L7 and its stream
// routes can no longer bind. Plugins must survive too.
func TestConfigDump_L4ServiceKeepsTypeAndPlugins(t *testing.T) {
reg := &httpmock.Registry{}
registerEmptyResources(reg, map[string]bool{"/apisix/admin/services": true})
reg.Register(http.MethodGet, "/apisix/admin/services", httpmock.JSONResponse(`{
"total": 1,
"list": [{
"id": "l4-svc",
"name": "tcp-svc",
"type": "stream",
"plugins": {"limit-conn": {"conn": 10, "burst": 5, "default_conn_delay": 0.1, "key": "remote_addr"}},
"upstream": {"type": "roundrobin", "nodes": [{"host": "10.0.0.1", "port": 6379, "weight": 1}]}
}]
}`))
// Routes are fetched per-service; return none for the stream service.
reg.Register(http.MethodGet, "/apisix/admin/routes", httpmock.JSONResponse(`{"total":0,"list":[]}`))

ios, _, stdout, _ := iostreams.Test()

c := NewCmdDump(newFactory(reg, ios))
c.SetArgs([]string{"--output", "json"})
require.NoError(t, c.Execute())

var result map[string]interface{}
require.NoError(t, json.Unmarshal(stdout.Bytes(), &result))

services := result["services"].([]interface{})
require.Len(t, services, 1)
svc := services[0].(map[string]interface{})
assert.Equal(t, "stream", svc["type"], "L4 service type must survive export")
plugins := svc["plugins"].(map[string]interface{})
assert.Contains(t, plugins, "limit-conn", "L4 service plugins must survive export")
reg.Verify(t)
}
Loading