Skip to content

Commit ad0e7b9

Browse files
committed
ProxyStrategies: code cleanup and improve test coverage.
1 parent ba83ab9 commit ad0e7b9

File tree

5 files changed

+137
-33
lines changed

5 files changed

+137
-33
lines changed

cmd/server/app/options/options.go

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package options
1919
import (
2020
"fmt"
2121
"os"
22-
"strings"
2322
"time"
2423

2524
"github.com/google/uuid"
@@ -90,10 +89,10 @@ type ProxyRunOptions struct {
9089

9190
// Proxy strategies used by the server.
9291
// NOTE the order of the strategies matters. e.g., for list
93-
// "destHost,destCIDR", the server will try to find a backend associating
92+
// "destHost,destCIDR,default", the server will try to find a backend associating
9493
// to the destination host first, if not found, it will try to find a
9594
// backend within the destCIDR. if it still can't find any backend,
96-
// it will use the default backend manager to choose a random backend.
95+
// it will choose a random backend.
9796
ProxyStrategies string
9897

9998
// Cipher suites used by the server.
@@ -135,7 +134,7 @@ func (o *ProxyRunOptions) Flags() *pflag.FlagSet {
135134
flags.Float32Var(&o.KubeconfigQPS, "kubeconfig-qps", o.KubeconfigQPS, "Maximum client QPS (proxy server uses this client to authenticate agent tokens).")
136135
flags.IntVar(&o.KubeconfigBurst, "kubeconfig-burst", o.KubeconfigBurst, "Maximum client burst (proxy server uses this client to authenticate agent tokens).")
137136
flags.StringVar(&o.AuthenticationAudience, "authentication-audience", o.AuthenticationAudience, "Expected agent's token authentication audience (used with agent-namespace, agent-service-account, kubeconfig).")
138-
flags.StringVar(&o.ProxyStrategies, "proxy-strategies", o.ProxyStrategies, "The list of proxy strategies used by the server to pick a backend/tunnel, available strategies are: default, destHost.")
137+
flags.StringVar(&o.ProxyStrategies, "proxy-strategies", o.ProxyStrategies, "The list of proxy strategies used by the server to pick an agent/tunnel, available strategies are: default, destHost, defaultRoute.")
139138
flags.StringSliceVar(&o.CipherSuites, "cipher-suites", o.CipherSuites, "The comma separated list of allowed cipher suites. Has no effect on TLS1.3. Empty means allow default list.")
140139

141140
flags.Bool("warn-on-channel-limit", true, "This behavior is now thread safe and always on. This flag will be removed in a future release.")
@@ -292,17 +291,8 @@ func (o *ProxyRunOptions) Validate() error {
292291
}
293292

294293
// validate the proxy strategies
295-
if o.ProxyStrategies != "" {
296-
pss := strings.Split(o.ProxyStrategies, ",")
297-
for _, ps := range pss {
298-
switch ps {
299-
case string(server.ProxyStrategyDestHost):
300-
case string(server.ProxyStrategyDefault):
301-
case string(server.ProxyStrategyDefaultRoute):
302-
default:
303-
return fmt.Errorf("unknown proxy strategy: %s, available strategy are: default, destHost, defaultRoute", ps)
304-
}
305-
}
294+
if _, err := server.ParseProxyStrategies(o.ProxyStrategies); err != nil {
295+
return fmt.Errorf("invalid proxy strategies: %v", err)
306296
}
307297

308298
// validate the cipher suites

cmd/server/app/options/options_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ func TestValidate(t *testing.T) {
145145
value: "TLS_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
146146
expected: nil,
147147
},
148+
"Empty proxy strategies": {
149+
field: "ProxyStrategies",
150+
value: "",
151+
expected: fmt.Errorf("invalid proxy strategies: proxy strategies cannot be empty"),
152+
},
148153
} {
149154
t.Run(desc, func(t *testing.T) {
150155
testServerOptions := NewProxyRunOptions()

cmd/server/app/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func (p *Proxy) Run(o *options.ProxyRunOptions, stopCh <-chan struct{}) error {
128128
AuthenticationAudience: o.AuthenticationAudience,
129129
}
130130
klog.V(1).Infoln("Starting frontend server for client connections.")
131-
ps, err := server.GenProxyStrategiesFromStr(o.ProxyStrategies)
131+
ps, err := server.ParseProxyStrategies(o.ProxyStrategies)
132132
if err != nil {
133133
return err
134134
}

pkg/server/backend_manager.go

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,40 +35,63 @@ import (
3535
"sigs.k8s.io/apiserver-network-proxy/proto/header"
3636
)
3737

38-
type ProxyStrategy string
38+
type ProxyStrategy int
3939

4040
const (
4141
// With this strategy the Proxy Server will randomly pick a backend from
4242
// the current healthy backends to establish the tunnel over which to
4343
// forward requests.
44-
ProxyStrategyDefault ProxyStrategy = "default"
44+
ProxyStrategyDefault ProxyStrategy = iota + 1
4545
// With this strategy the Proxy Server will pick a backend that has the same
4646
// associated host as the request.Host to establish the tunnel.
47-
ProxyStrategyDestHost ProxyStrategy = "destHost"
48-
47+
ProxyStrategyDestHost
4948
// ProxyStrategyDefaultRoute will only forward traffic to agents that have explicity advertised
5049
// they serve the default route through an agent identifier. Typically used in combination with destHost
51-
ProxyStrategyDefaultRoute ProxyStrategy = "defaultRoute"
50+
ProxyStrategyDefaultRoute
5251
)
5352

53+
func (ps ProxyStrategy) String() string {
54+
return [...]string{"default", "destHost", "defaultRoute"}[ps-1]
55+
}
56+
57+
func ParseProxyStrategy(s string) (ProxyStrategy, error) {
58+
switch s {
59+
case ProxyStrategyDestHost.String():
60+
return ProxyStrategyDestHost, nil
61+
case ProxyStrategyDefault.String():
62+
return ProxyStrategyDefault, nil
63+
case ProxyStrategyDefaultRoute.String():
64+
return ProxyStrategyDefaultRoute, nil
65+
default:
66+
return 0, fmt.Errorf("unknown proxy strategy: %s", s)
67+
}
68+
}
69+
5470
// GenProxyStrategiesFromStr generates the list of proxy strategies from the
5571
// comma-seperated string, i.e., destHost.
56-
func GenProxyStrategiesFromStr(proxyStrategies string) ([]ProxyStrategy, error) {
57-
var ps []ProxyStrategy
72+
func ParseProxyStrategies(proxyStrategies string) ([]ProxyStrategy, error) {
73+
var result []ProxyStrategy
74+
seen := map[ProxyStrategy]bool{}
75+
5876
strs := strings.Split(proxyStrategies, ",")
5977
for _, s := range strs {
60-
switch s {
61-
case string(ProxyStrategyDestHost):
62-
ps = append(ps, ProxyStrategyDestHost)
63-
case string(ProxyStrategyDefault):
64-
ps = append(ps, ProxyStrategyDefault)
65-
case string(ProxyStrategyDefaultRoute):
66-
ps = append(ps, ProxyStrategyDefaultRoute)
67-
default:
68-
return nil, fmt.Errorf("Unknown proxy strategy %s", s)
78+
if len(s) == 0 {
79+
continue
80+
}
81+
ps, err := ParseProxyStrategy(s)
82+
if err != nil {
83+
return nil, err
6984
}
85+
if _, ok := seen[ps]; ok {
86+
return nil, fmt.Errorf("duplicate proxy strategy: %s", ps)
87+
}
88+
seen[ps] = true
89+
result = append(result, ps)
90+
}
91+
if len(result) == 0 {
92+
return nil, fmt.Errorf("proxy strategies cannot be empty")
7093
}
71-
return ps, nil
94+
return result, nil
7295
}
7396

7497
// Backend abstracts a connected Konnectivity agent.

pkg/server/backend_manager_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,89 @@ func TestDestHostBackendManager_WithDuplicateIdents(t *testing.T) {
426426
t.Errorf("expected %v, got %v", e, a)
427427
}
428428
}
429+
430+
func TestParseProxyStrategy(t *testing.T) {
431+
for desc, tc := range map[string]struct {
432+
input string
433+
want ProxyStrategy
434+
wantErr bool
435+
}{
436+
"empty": {
437+
input: "",
438+
wantErr: true,
439+
},
440+
"unrecognized": {
441+
input: "unrecognized",
442+
wantErr: true,
443+
},
444+
"default": {
445+
input: "default",
446+
want: ProxyStrategyDefault,
447+
},
448+
"destHost": {
449+
input: "destHost",
450+
want: ProxyStrategyDestHost,
451+
},
452+
"defaultRoute": {
453+
input: "defaultRoute",
454+
want: ProxyStrategyDefaultRoute,
455+
},
456+
} {
457+
t.Run(desc, func(t *testing.T) {
458+
got, err := ParseProxyStrategy(tc.input)
459+
if tc.wantErr != (err != nil) {
460+
t.Errorf("ParseProxyStrategy(%s) error: got error %q, want %v", tc.input, err, tc.wantErr)
461+
}
462+
if err == nil && got != tc.want {
463+
t.Errorf("ParseProxyStrategy(%s): got %v, want %v", tc.input, got, tc.want)
464+
}
465+
})
466+
}
467+
}
468+
469+
func TestParseProxyStrategies(t *testing.T) {
470+
for desc, tc := range map[string]struct {
471+
input string
472+
want []ProxyStrategy
473+
wantErr bool
474+
}{
475+
"empty": {
476+
input: "",
477+
wantErr: true,
478+
},
479+
"unrecognized": {
480+
input: "unrecognized",
481+
wantErr: true,
482+
},
483+
"default": {
484+
input: "default",
485+
want: []ProxyStrategy{ProxyStrategyDefault},
486+
},
487+
"destHost": {
488+
input: "destHost",
489+
want: []ProxyStrategy{ProxyStrategyDestHost},
490+
},
491+
"defaultRoute": {
492+
input: "defaultRoute",
493+
want: []ProxyStrategy{ProxyStrategyDefaultRoute},
494+
},
495+
"duplicate": {
496+
input: "destHost,defaultRoute,defaultRoute,default",
497+
wantErr: true,
498+
},
499+
"multiple": {
500+
input: "destHost,defaultRoute,default",
501+
want: []ProxyStrategy{ProxyStrategyDestHost, ProxyStrategyDefaultRoute, ProxyStrategyDefault},
502+
},
503+
} {
504+
t.Run(desc, func(t *testing.T) {
505+
got, err := ParseProxyStrategies(tc.input)
506+
if tc.wantErr != (err != nil) {
507+
t.Errorf("ParseProxyStrategy(%s) error: got error %q, want %v", tc.input, err, tc.wantErr)
508+
}
509+
if !reflect.DeepEqual(got, tc.want) {
510+
t.Errorf("ParseProxyStrategy(%s): got %v, want %v", tc.input, got, tc.want)
511+
}
512+
})
513+
}
514+
}

0 commit comments

Comments
 (0)